本文全部參照: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