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的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_values
:fieldData
始终存放在 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%
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