elasticsearch CircuitBreakingException FIELDDATA Data too large 異常


本文全部參照:https://blog.csdn.net/hereiskxm/article/details/46744985  這里自己留底記錄

1. 產生Data too large異常

異常如下:CircuitBreakingException[[FIELDDATA] Data too large, data for [proccessDate] would be larger than limit of [xxxgb]

經排查,原來是ES默認的緩存設置讓緩存區只進不出引起的,具體分析一下。

2. ES緩存區概述

首先簡單描述一下ES的緩存機制。ES在查詢時,會將索引數據緩存在內存(JVM)中: 

驅逐線 和 斷路器。當緩存數據到達驅逐線時,會自動驅逐掉部分數據,把緩存保持在安全的范圍內。當用戶准備執行某個查詢操作時,斷路器就起作用了,緩存數據+當前查詢需要緩存的數據量到達斷路器限制時,會返回Data too large錯誤,阻止用戶進行這個查詢操作。ES把緩存數據分成兩類,FieldData和其他數據,我們接下來詳細看FieldData,它是造成我們這次異常的“元凶”。

ES配置中提到的FieldData指的是字段數據。當排序(sort),統計(aggs)時,ES把涉及到的字段數據全部讀取到內存(JVM Heap)中進行操作。相當於進行了數據緩存,提升查詢效率

3.1 監控FieldData

仔細監控fielddata使用了多少內存以及是否有數據被驅逐是非常重要的。

Fielddata緩存使用可以通過下面的方式來監控:

對於單個索引使用
GET /_stats/fielddata?fields=*

對於單個節點使用
GET /_nodes/stats/indices/fielddata?fields=*

或者甚至單個節點單個索引
GET /_nodes/stats/indices/fielddata?level=indices&fields=*

通過設置 ?fields=* 內存使用按照每個字段分解了.
在產生Data too large異常時,可以看到 memory_size_in_bytes 用到了整個JVM內存的60%(可用上限),而evictions(驅逐)為0。且經過一段時間觀察,字段所占內存大小都沒有變化。由此推斷,當下的緩存處於無法有效驅逐的狀態。

3.2 Cache配置

關於FieldData的配置在elasticsearch.yml中,也可以通過調用setting接口來修改。API文檔里的介紹如下:

indices.fielddata.cache.size:The max size of the field data cache, eg 30% of node heap space, or an absolute value, eg 12GB. Defaults to unbounded.
indices.fielddata.cache.expire:[experimental] This functionality is experimental and may be changed or removed completely in a future release. A time based setting that expires field data after a certain time of inactivity. Defaults to -1. For example, can be set to 5m for a 5 minute expiry.

簡而言之: 
indices.fielddata.cache.size   配置fieldData的Cache大小,可以配百分比也可以配一個准確的數值。cache到達約定的內存大小時會自動清理,驅逐一部分FieldData數據以便容納新數據。默認值為unbounded無限。 
indices.fielddata.cache.expire  用於約定多久沒有訪問到的數據會被驅逐,默認值為-1,即無限。expire配置不推薦使用,按時間驅逐數據會大量消耗性能。而且這個設置在不久之后的版本中將會廢棄。
看來,Data too large異常就是由於fielddata.cache的默認值為unbounded導致的了。

3.3 FieldData格式

除了緩存取大小之外,我們還可以控制字段數據緩存到內存中的格式。

在mapping中,我們可以這樣設置:

1 {
2     "tag": {
3         "type":      "string",
4         "fielddata": {
5             "format": "fst"
6         }
7     }
8 }

對於String類型,format有以下幾種:

paged_bytes       (默認值) 使用大量的內存來存儲這個字段的terms和索引。
fst               用`FST`的形式來存儲terms。這在terms有較多共同前綴的情況下可以節約使用的內存,但訪問速度上比paged_bytes 要慢。
doc_values        fieldData 始終存放在disk中,不加載進內存。訪問速度最慢且只有在index:no/not_analyzed的情況適用。

對於數字和地理數據也有可選的format,但相對String更為簡單,具體可在api中查看。
從上面我們可以得知一個信息:我們除了配置緩存區大小以外,還可以對不是特別重要卻量很大的String類型字段選擇使用fst緩存類型來壓縮大小。

4. 斷路器

fieldData的緩存配置中,有一個點會引起我們的疑問:fielddata的大小是在數據被加載之后才校驗的。假如下一個查詢准備加載進來的fieldData讓緩存區超過可用堆大小會發生什么?很遺憾的是,它將產生一個OOM異常。
斷路器就是用來控制cache加載的,它預估當前查詢申請使用內存的量,並加以限制。斷路器的配置如下:

indices.breaker.fielddata.limit 
這個 fielddata 斷路器限制fielddata的大小,默認情況下為堆大小的60%
indices.breaker.request.limit
這個 request 斷路器估算完成查詢的其他部分要求的結構的大小, 默認情況下限制它們到堆大小的40
%

indices.breaker.total.limit
這個 total 斷路器封裝了 request 和 fielddata 斷路器去確保默認情況下這2個部分使用的總內存不超過堆大小的70
%。

斷路器限制可以通過文件 config/elasticsearch.yml 指定,也可以在集群上動態更新:

PUT /_cluster/settings

{
  "persistent" : {
    "indices.breaker.fielddata.limit" : 40% (1)
  }
}

當緩存區大小到達斷路器所配置的大小時會發生什么事呢?答案是:會返回開頭我們說的Data too large異常。這個設定是希望引起用戶對ES服務的反思,我們的配置有問題嗎?是不是查詢語句的形式不對,一條查詢語句需要使用這么多緩存嗎?

5. 總結

1. 這次Data too large異常是ES默認配置的一個坑,我們沒有配置indices.fielddata.cache.size,它就不回收緩存了。緩存到達限制大小,無法往里插入數據。個人感覺這個默認配置不友好,不知ES是否在未來版本有所改進。
2. 當前fieldData緩存區大小
< indices.fielddata.cache.size   當前fieldData緩存區大小+下一個查詢加載進來的fieldData < indices.breaker.fielddata.limit   fielddata.limit的配置需要比fielddata.cache.size稍大。而fieldData緩存到達fielddata.cache.size的時候就會啟動自動清理機制。expire配置不建議使用。 3. indices.breaker.request.limit限制查詢的其他部分需要用的內存大小。indices.breaker.total.limit限制總(fieldData+其他部分)大小。 4. 創建mapping時,可以設置fieldData format控制緩存數據格式。

5. 查詢使用 sort、agg 是考慮使用字段的內存,慎用sort、agg

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM