題記
Elasticsearch 社區有大量關於 Elasticsearch 錯誤和異常的問題。
深挖這些錯誤背后的原因,把常見的錯誤積累為自己的實戰經驗甚至是工具,不僅可以節省我們的開發和運維時間,而且可以幫助確保 Elasticsearch 集群的長期健康運行。
常見的異常、原因和常規最佳實踐拆解如下,這些最佳實踐可以幫助我們更有效地識別、最小化定位和處理異常問題。
1、 Mapper_parsing_exception
Elasticsearch 依靠映射(Mapping)定義的數據類型處理數據。
映射定義了文檔中的字段並指定了它們對應的數據類型,例如日期類型 Date、長整數類型 long 和 字符串類型 text。
如果索引文檔包含沒有定義數據類型的新字段,Elasticsearch將使用動態映射來估計字段的類型,並在必要時將其從一種類型轉換為另一種類型。
如果Elasticsearch無法執行此轉換,它將引發“ mapper_parsing_exception無法解析” 異常。
如果此類異常太多會降低索引吞吐量。
實戰舉例如下:
DELETE mytest_0001
PUT mytest_0001/_doc/1
{
"name":"John"
}
PUT mytest_0001/_doc/2
{
"name": {
"firstname": "John",
"lastname": "doe"
}
}
為避免此問題,可以在創建索引時顯示定義Mapping,明確敲定字段類型。或者可以使用 _mapping 動態添加新字段映射。
動態更新索引實戰:
PUT mytest_0001/_mapping
{
"properties": {
"title": {
"type": "text"
}
}
}
請注意:雖然可以通過如上命令動態添加字段,但是不能更改現有字段映射。
若想做字段類型的修改,需要重新定義Mapping 結合 reindex 和 alias 別名 實現。
2、BulkIndexError
批量索引大型數據集通常更有效。
例如,您可以執行一個批量操作來索引 1,000 個文檔,而不是使用 1,000 個索引操作。
批量操作可以通過 bulk API 完成。
批量操作實戰:
PUT my_index_0003/_bulk
{"index":{"_id":1}}
{"myid":"c12345"}
{"index":{"_id":2}}
{"myid":"C12456"}
{"index":{"_id":3}}
{"myid":"C31268"}
但是,此過程容易出錯。執行批量操作的過程中,你需要仔細檢查:數據類型不匹配和空值匹配等問題。
對於批量 API ,你需要格外警惕,因為即使有數百個肯定的響應,批量中的某些索引請求也可能失敗。
批量操作捕獲錯誤實戰:
@Override
public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
if (response.hasFailures()) {
for (int i = 0; i < response.getItems().length; i++) {
BulkItemResponse item = response.getItems()[i];
if (item.isFailed()) {
IndexRequest ireq = (IndexRequest) request.requests().get(i);
logger.error("Failed while indexing to " + item.getIndex() + " type " + item.getType() + " " +
"request: [" + ireq + "]: [" + item.getFailureMessage() + "]");
}
}
}
}
除了提前設置具有所有適當條件的批量 API 之外,還要瀏覽響應列表並檢查每個響應,以確保所有數據均按預期索引。
3、搜索超時錯誤:ConnectionTimeout,ReadTimeoutError,RequestTimeout 等
如果在指定的搜索時間內未收到響應,則請求將失敗並返回錯誤消息。這稱為搜索超時。
搜索超時很常見,多種原因都可以導致搜索超時,例如:大型數據集或占用大量內存的查詢。
要消除搜索超時,可以通過如下實現解決:
3.1 增加 elasticsearch.requestTimeout
設置注意:應該在 HTTP 客戶端而不是 Elasticsearch 中指定 timeout 值,Elasticsearch 端沒有請求超時參數。
kibana 請求顯示超時,優化方案如下:
kibana 默認請求等待時間是 30 秒,可以在 kibana.yml 中調整該值。
elasticsearch.requestTimeout: 90000
3.2 減少每個請求返回的文檔數量
不要將請求的 size 值設置太大,結合:from、size 深度翻頁機制實現。
全量遍歷借助 scroll 實現。
3.3 縮小時間范圍
請求時間范圍越長(比如 時間跨度周期 1 年以上的數據),請求數據量越大,超時的可能性越高。
3.4 調整內存設置
通過配置單個查詢的內存斷路器來限制單個查詢的內存使用量。
如:將 index.breaker.request.limit 限制為 40%,默認是 60%。
集群層面設置請求熔斷內存實戰:
PUT /_cluster/settings
{
"persistent": {
"indices.breaker.request.limit": "40%"
}
}
通過將search.max_buckets設置為 5000 (默認值:10000)來限制用於聚合的存儲桶數。
PUT _cluster/settings
{
"transient": {
"search.max_buckets": 5000
}
}
3.5 優化查詢、索引和分片。
3.6 啟用慢速搜索日志
監視搜索運行時間,掃描繁重的搜索等等。
慢日志開啟實戰:
PUT /_settings
{
"index.search.slowlog.threshold.query.debug": "30s",
"index.search.slowlog.threshold.fetch.debug": "30s",
"index.indexing.slowlog.threshold.index.debug": "30s"
}
4、 All Shards Failed
在 Elasticsearch 搜索時,可能會遇到 “All Shards Failed” 的錯誤消息。
發生 All Shards Failed 的幾種情況:
- 當讀取請求無法從分片獲得響應時
- 當由於集群或節點仍處於初始啟動過程而無法搜索數據
- 當分片丟失或處於恢復模式並且集群為紅色時
造成 All Shards Failed 可能的原因:
- 節點可能已斷開連接或重新連接
- 正在查詢的分片可能正在恢復中,因此不可用
- 磁盤可能已損壞
- 搜索query 語句可能寫的有問題。例如,引用字段類型錯誤的字段。
- 配置錯誤可能導致操作失敗。
問題排查實戰舉例:
GET /_cat/health
GET /_cat/indices?v
GET _cluster/health/?level=shards
GET _cluster/allocation/explain
5、進程內存鎖定失敗:“memory locking requested for elasticsearch process but memory is not locked”
為了使節點保持健康,必須確保沒有將 JVM 內存換出到磁盤。
發生系統 swapping (交換)的時候 Elasticsearch 節點的性能會非常差,也會影響節點的穩定性。
所以要不惜一切代價來避免 swapping 。swapping會導致Java GC的周期延遲從毫秒級惡化到分鍾,更嚴重的是會引起節點響應延遲甚至脫離集群。
限制 elasticsearch占用的內存情況,可選擇少用swap。而:啟用 bootstrap.memory_lock 就是限制交換的三種方案之一。
在 elasticsearch.yml 中 啟動 memory_lock 實踐:
bootstrap.memory_lock: true
報錯復現如下:
[,260][INFO ][o.e.n.Node ] [node-1] starting ...
[,529][INFO ][o.e.t.TransportService ] [node-1] publish_address {172.17.0.5:9300}, bound_addresses {172.17.0.5:9300}
[,537][INFO ][o.e.b.BootstrapChecks ] [node-1] bound or publishing to a non-loopback address, enforcing bootstrap checks
[,565][ERROR][o.e.b.Bootstrap ] [node-1] node validation exception
[1] bootstrap checks failed
[1]: memory locking requested for elasticsearch process but memory is not locked
[,575][INFO ][o.e.n.Node ] [node-1] stopping ...
[,596][INFO ][o.e.n.Node ] [node-1] stopped
[,597][INFO ][o.e.n.Node ] [node-1] closing ...
[,615][INFO ][o.e.n.Node ] [node-1] closed
centos 7.x 解決方案:在 /etc/security/limits.conf 文件中添加如下內容,並保持,然后重啟 elasticsearch 即可。
elasticsearch soft memlock unlimited
elasticsearch hard memlock unlimited
最佳實踐之驗證啟動是否成功:
GET _nodes?filter_path=**.mlockall
正確返回結果如下:
{
"nodes" : {
"gJUT-E48u_nUw" : {
"process" : {
"mlockall" : true
}
}
}
}
6、引導檢查失敗 Bootstrap Checks Failed
Bootstrap 檢查會在 Elasticsearch 開始之前檢查各種設置和配置,以確保其可以安全運行。
如果引導檢查失敗,則它們可以阻止 Elasticsearch 啟動(如果處於生產模式)或在開發模式下發出警告日志。
建議你熟悉引導檢查所強制執行的設置,並注意它們在開發和生產模式上是不同的。通過將系統屬性
es.enforce.bootstrap.checks設置為true,可以強制執行引導檢查。
主要檢查內容包含但不限於:
- 堆的大小檢查
- 文件描述符
- 最大線程數
- 文件大小限制
- 最大虛擬內存
- 最大映射數
- 客戶端jvm檢查
- 垃圾收集檢查
- OnError和OnOutOfMemoryError檢查 ......
最佳實踐:在 jvm.option 中添加如下配置后重啟 Elasticsearch。
-Des.enforce.bootstrap.checks=true
7、TransportError
在Elasticsearch中,傳輸模塊核心功能是:集群中節點之間的通信。
傳輸錯誤Transport errors 經常出現,失敗可能是如下的原因引起的:
-
分片丟失
-
設置沖突
-
數據建模不合理
-
網絡故障
-
.....
常見的 Transport errors 錯誤如下:
TransportError(403, u'cluster_block_exception', u'blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];')
原因分析:
當沒有足夠的可用磁盤空間供 Elasticsearch 在節點之間分配時,可能會發生這種情況。
解決方案:
- 增加磁盤空間
- 刪除舊數據以釋放空間
- 更新索引只讀模式。
注意:當磁盤使用率>=95%,index.blocks.read_only_allow_delete設置是防止節點用完磁盤空間的最后手段。不再允許寫入,只能刪除。
以下命令能重置索引上的只讀索引塊:
PUT /_all/_settings
{
"index.blocks.read_only_allow_delete": null
}
在分配所有分片之前,嘗試使用剛剛創建的索引時,可能會出現另一種傳輸錯誤。
在這種情況下,報錯如下:
TransportError(503, u”).
傳輸錯誤也可能與 Mapping 問題相關。
例如,當您嘗試索引具有與其映射不同的數據類型的字段時,可能報錯如下:
TransportError (400, u’mapper_pasing_exception’)
8、初始化/啟動失敗 Initialization/Startup Failures
有時候,分片的問題可能會阻止 Elasticsearch 啟動。
例如,當使用有沖突的 Elasticsearch 版本時,您可能報錯如下:
“ Elasticsearch java client initialization fails”
或
“\Common was unexpected at this time.”
最佳實踐:
做好版本核驗,確保開發使用的 jar 包版本和部署版本一致。
9、如何最小化錯誤和異常?探究錯誤及解決方案的底層邏輯
如果你不想僅僅一次處理一條錯誤消息,當你處理的問題多了以后,你會發現:很多錯誤和異常與如下三個更深層次的問題相關:
- 安裝和配置問題
- 索引新數據問題
- 集群運行變慢問題
深究拆解如下:
9.1 安裝和配置問題
快速安裝 Elasticsearch 很容易,但是要確保其生產級別的運行,需要仔細核對配置。
這可以幫助避免各種錯誤和異常,例如:引導檢查失敗 bootstrap checks failure 問題。
9.2 索引新數據問題
在 Elasticsearch 中,你必須非常仔細的對字段命名、正確使用模板 template、數據建模規范化。
仔細核對這些參數配置,可以幫助你避免諸如:映射 mapping 異常和批量索引錯誤( bulk index errors)之類的問題。
9.3 集群速度變慢問題
隨着數據規模的擴大,以及操作頻繁度的擴展,Elasticsearch 有時會發生意外導致檢索響應速度慢,並可能彈出超時報錯。
因此,你必須持續監控集群的如下指標內容:
- 借助 kibana 或者 cerebro 等可視化工具觀察錯誤率及走勢
- 監控錯誤日志
- 核對拒絕的指標
以提前將可能錯誤扼殺在搖籃階段,並確保集群一切正常。
10、結論
Elasticsearch 運維或開發實戰必定會遇到錯誤或異常。
盡管我們無法完全避免,但是可以采用一些最佳實踐來幫助減少錯誤或異常的發生,並在出現問題時更有效地解決問題。
快速有效地解決集群緩慢等復雜問題離不開如下三點:
第一:密切關注各項設置和配置;
第二:索引新數據時要小心;
第三:確保集群各項指標可被監視與可視化查看。
簡而言之,你應該將錯誤和異常視為優化 Elasticsearch 集群基礎架構的機會,而不必過分擔心它們的出現。