ElasticSearch 429 Too Many Requests circuit_breaking_exception

Error messagehtml

{
  "statusCode": 429,
  "error": "Too Many Requests",
  "message": "[circuit_breaking_exception] 
  [parent] Data too large, data for [<http_request>] would be [2087772160/1.9gb], 
  which is larger than the limit of [1503238553/1.3gb], 
  real usage: [2087772160/1.9gb],
  new bytes reserved: [0/0b], 
  usages [request=0/0b, fielddata=1219/1.1kb, in_flight_requests=0/0b, accounting=605971/591.7kb], 
  with { bytes_wanted=2087772160 & bytes_limit=1503238553 & durability=\"PERMANENT\" }"
}

此時 kibana 已經沒法訪問,因此必須登錄服務器排查問題。下面先給出解決方法,後介紹問題緣由。node

解決方案

查看服務狀態json

# supervisorctl status

elasticSearch                    RUNNING   pid 1838, uptime 6:03:37

jaeger-collector                 RUNNING   pid 2572, uptime 6:03:10

jaeger-query                     RUNNING   pid 2571, uptime 6:03:10

kibana                           RUNNING   pid 5048, uptime 5:42:22

發現ES及其它服務運行狀態都正常。api

查看節點狀態 (略)緩存

curl -XGET '[http://localhost:9200/\_nodes/stats?pretty](http://localhost:9200/_nodes/stats?pretty)' |less

清理fileddata cache安全

curl -XPOST '[http://localhost:9200/\_all/\_cache/clear?fielddata=true](http://localhost:9200/_all/_cache/clear?fielddata=true)'

嘗試清理fileddata cache後,發現服務仍然沒法正常訪問。服務器

查看全部indexsapp

curl -X GET '[http://127.0.0.1:9200/\_cat/indices?v](http://127.0.0.1:9200/_cat/indices?v)'

刪除全部 jaeger開頭 的index
若是你的數據是日誌或非重要的數據,能夠把相關的index先刪除。less

curl -X DELETE "localhost:9200/jaeger\*?pretty"

修改fielddata配置curl

curl -H "Content-Type: application/json" -XPUT '[http://localhost:9200/\_cluster/settings](http://localhost:9200/_cluster/settings)' -d '{

  "persistent" : {

    "indices.breaker.fielddata.limit":"40%"

  }

}'

這個限制是按對內存的百分比設置的。fielddata斷路器默認設置堆的 60% 做爲 fielddata 大小的上限。

解決問題的關鍵點在這,修改 indices.breaker.fielddata.limit 的默認配置。修改後重啓服務器就OK了。

重啓服務

supervisorctl restart elasticSearch && supervisorctl restart jaeger-collector && supervisorctl restart jaeger-query && supervisorctl restart kibana

問題分析

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

  • ES緩存區概述
  • ES在查詢時,會將索引數據緩存在內存(JVM)中:

794174-20191204133916360-1049841851.png

上圖是ES的JVM Heap中的情況,能夠看到有兩條界限:驅逐線 和 斷路器。當緩存數據到達驅逐線時,會自動驅逐掉部分數據,把緩存保持在安全的範圍內。

當用戶準備執行某個查詢操做時,斷路器就起做用了,緩存數據+當前查詢須要緩存的數據量到達斷路器限制時,會返回Data too large錯誤,阻止用戶進行這個查詢操做。

ES把緩存數據分紅兩類,FieldData和其餘數據,咱們接下來詳細看FieldData,它是形成咱們此次異常的「元兇」。

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

監控FieldData
仔細監控fielddata使用了多少內存以及是否有數據被驅逐是很是重要的。 ielddata緩存使用能夠經過下面的方式來監控

# 對於單個索引使用 {ref}indices-stats.html[indices-stats API]

GET /_stats/fielddata?fields=*

# 對於單個節點使用 {ref}cluster-nodes-stats.html[nodes-stats API]

GET /_nodes/stats/indices/fielddata?fields=*

#或者甚至單個節點單個索引

GET /_nodes/stats/indices/fielddata?level=indices&fields=*

# 經過設置 ?fields=* 內存使用按照每一個字段分解了

fielddata中的memory_size_in_bytes表示已使用的內存總數,而evictions(驅逐)爲0。且通過一段時間觀察,字段所佔內存大小都沒有變化。由此推斷,當下的緩存處於沒法有效驅逐的狀態。

Cache配置
indices.fielddata.cache.size 配置fieldData的Cache大小,能夠配百分比也能夠配一個準確的數值。cache到達約定的內存大小時會自動清理,驅逐一部分FieldData數據以便容納新數據。默認值爲unbounded無限。

indices.fielddata.cache.expire用於約定多久沒有訪問到的數據會被驅逐,默認值爲-1,即無限。expire配置不推薦使用,按時間驅逐數據會大量消耗性能。並且這個設置在不久以後的版本中將會廢棄。

看來,Data too large異常就是因爲fielddata.cache的默認值爲unbounded致使的了。

FieldData格式
除了緩存取大小以外,咱們還能夠控制字段數據緩存到內存中的格式。

mapping中,咱們能夠這樣設置:

{
    "tag": {
        "type":      "string",
        "fielddata": {
            "format": "fst"
        }
    }
}

對於String類型,format 有如下幾種:
paged_bytes (默認):使用大量的內存來存儲這個字段的terms和索引。
fst:用 FST 的形式來存儲 terms。這在 terms 有較多共同前綴的狀況下能夠節約使用的內存,但訪問速度上比 paged_bytes 要慢。
doc_valuesfieldData 始終存放在 disk 中,不加載進內存。訪問速度最慢且只有在 index:no/not_analyzed 的狀況適用。

對於數字和地理數據也有可選的 format,但相對 String 更爲簡單,具體可在api中查看。

從上面咱們能夠得知一個信息:咱們除了配置緩存區大小之外,還能夠對不是特別重要卻量很大的 String 類型字段選擇使用 fst 緩存類型來壓縮大小。

斷路器
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%。

查詢

/_cluster/settings

設置

PUT /_cluster/settings
{
  "persistent": {
    "indices.breaker.fielddata.limit": "60%"
  }
} 


PUT /_cluster/settings
{
  "persistent": {
    "indices.breaker.request.limit": "40%"
  }
} 


PUT /_cluster/settings
{
  "persistent": {
    "indices.breaker.total.limit": "70%"
  }
}

斷路器限制能夠經過文件 config/elasticsearch.yml 指定,也能夠在集羣上動態更新:

PUT /_cluster/settings
{
  "persistent" : {
    "indices.breaker.fielddata.limit" : 40% 
  }
}

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

在文件 config/elasticsearch.yml 文件中設置緩存使用回收

indices.fielddata.cache.size:  40%

總結

  • 此次Data too large異常是ES默認配置的一個坑,咱們沒有配置indices.fielddata.cache.size,它就不回收緩存了。緩存到達限制大小,沒法往裏插入數據。我的感受這個默認配置不友好,不知ES是否在將來版本有所改進。
  • 當前fieldData緩存區大小 < indices.fielddata.cache.size
  • 當前fieldData緩存區大小+下一個查詢加載進來的fieldData < indices.breaker.fielddata.limit
  • fielddata.limit的配置須要比fielddata.cache.size稍大。而fieldData緩存到達fielddata.cache.size的時候就會啓動自動清理機制。expire配置不建議使用。
  • indices.breaker.request.limit限制查詢的其餘部分須要用的內存大小。indices.breaker.total.limit限制總(fieldData+其餘部分)大小。
  • 建立mapping時,能夠設置fieldData format控制緩存數據格式。

參考

https://www.codetd.com/en/article/8070413

https://elasticsearch.cn/question/6642

Doc Values and Fielddata限制內存使用

https://discuss.elastic.co/t/unable-to-start-kibana-and-circuit-breaking-exception/191860

https://www.cnblogs.com/sanduzxcvbnm/p/11982476.html

https://www.elastic.co/guide/en/elasticsearch/reference/6.5/indices-clearcache.html

相關文章
相關標籤/搜索