【Elasticsearch】Elasticsearch中使用_id排序导致 data too large 问题
前言
Elasticsearch 一个弹性伸缩的搜索数据库,后文简称 :ES ,最近有一个ES 服务查询数据时候出现了数据Hits
结果多次查询不一致的问题,而且这块代码已经很长时间没有修改,一直稳定运行了很长时间,用户翻译查询列表数据的时候又是出现1条,有时候出现2条或者3条。(再加上我们的ES 内存监控服务在次阶段进行了迁移。导致服务监控不可用,对于ES heap 堆内存过载的问题没有得到及时的反馈),于是紧急去排查了问题。在此记录排查问题后的一些总结。
以下是本篇文章正文内容,下面案例可供参考
一、出问题的代码
项目中使用了SpringData的NativeSearchQueryBuilder构造器进行查询。
/***********上面省略其他构造条件**************
//排序条件
FieldSortBuilder fsb = SortBuilders.fieldSort("_id").order(SortOrder.DESC);
//分页条件
Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
//构建查询
SearchQuery query = new NativeSearchQueryBuilder()
.withQuery(bqb)
.withSort(fsb)
.withPageable(pageable)
.build();
下面是错误休息由于个人原因不方便给出线上错误,此错误日志来自es官方文档,内容大致相同。
{
"error": "... CircuitBreakingException[[FIELDDATA] Data too large, data for [proccessDate] would
be larger than limit of [10307921510/9.5gb]]; }]",
"status": 500
}
通过以上代码看出,此处代码使用了Elasticsearch 中自带的_id 字段进行排序,问题就出现在这里。
回顾下前置知识(次内容摘自Elasticsearch内核解析 - 数据模型篇):
内容提到了 ES底层数据模型是Lucene数据模型,而Lucene数据模型包括了
- Index:索引,由很多的Document组成。
- Invert Index:倒排索引,或者简称Index,通过Term可以查询到拥有该Term的文档。可以配置为是否分词,如果 分词可以配置不同的分词器。索引存储的时候有多种存储类型,分别是:
- DOCS:只存储DocID。
- DOCS_AND_FREQS:存储DocID和词频(Term Freq)。
- DOCS_AND_FREQS_AND_POSITIONS:存储DocID、词频(Term Freq)和位置。
- DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS:存储DocID、词频(Term Freq)、位置和偏移。
- DocValues:正排索引,采用列式存储。通过DocID可以快速读取到该Doc的特定字段的值。由于是列式存储,性能会比较好。一般用于sort,agg等需要高频读取Doc字段值的场景。
- Store:字段原始内容存储,同一篇文章的多个Field的Store会存储在一起,适用于一次读取少量且多个字段内存的场景,比如摘要等。
- Document:由很多的Field组成,是Index和Search的最小单位。
- Field:由很多的Term组成,包括Field Name和Field Value。
- Term:由很多的字节组成,可以分词。
其中_id
(在ES中使用的就是Lucene的_uid) 这个字段,此片文章给出一张图:
通过上文了解到
:_id字段是一个物理字段,用于唯一标识每个文档。而在ES中 默认的_id 并不会生成 DocValues】既然不会使用doc_values 那就是需要用到FieldData了,这个在ES中默认是关闭状态,恰好我们的ES服务是打开了这个开关的,所以在根据这个字段进行排序的时候,ES就开始在Heap 中开始建立临时的正排索引,来保证查询效率,同样Feild也会常驻内存,JVM垃圾回收并不会回收此区域的内存。而查询官方文档也说到了此问题(一下一段话摘自ES官方文档):
fielddata
mapping parameter
text fields are searchable by default, but by default are not available for aggregations, sorting, or scripting. If you try to sort, aggregate, or access values from a script on a text field, you will see this exception:
Fielddata is disabled on text fields by default. Set fielddata=true on your_field_name in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory.
Field data is the only way to access the analyzed tokens from a full text field in aggregations, sorting, or scripting. For example, a full text field like New York would get analyzed as new and york. To aggregate on these tokens requires field data.
Before enabling `fielddata·
It usually doesn’t make sense to enable fielddata on text fields. Field data is stored in the heap with the field data cache because it is expensive to calculate(这是一个非常昂贵的计算). Calculating the field data can cause latency spikes, and increasing heap usage is a cause of cluster performance issues(增加堆使用量是导致集群性能问题的原因).
如何监控FieldData
ES给出了API:
对于单个索引使用 {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=*
以下是 FieldData 监控的各字段的解释:
-
evictions:表示当前索引字段数据缓存被清除的次数。如果这个数字太高,说明字段数据缓存的大小可能太小了,需要增加缓存大小或者调整缓存清除策略。
-
memory_size_in_bytes:表示当前字段数据缓存使用的内存大小,以字节为单位。
-
field_name:表示当前监控的字段名。
-
account_count:表示当前字段数据缓存中已经缓存的项数。如果这个数字太高,说明字段数据缓存的大小可能太小了,需要增加缓存大小或者调整缓存清除策略。
-
circuit_breaker_limit_size:表示当前字段数据缓存的断路器限制大小。如果这个数字太低,可能会导致查询缓慢或者OOM异常。需要根据查询场景合理调整。
-
circuit_breaker_limit_size_in_bytes:表示当前字段数据缓存的断路器限制大小,以字节为单位。
注意:以上信息仅供参考,具体情况需要根据实际场景进行分析。
欸?按说这种错误应该直接报错啊?为什么出现了数据一会儿多一会儿少的问题呢?
我在Elasticsearch的官方博客上看到了一个19年一个用户对于类似问题的进行反馈,我的一个思考。
文章的内容大致描述报错内容和我很相似,题中一直提到 circuit_breaking_exception,我去ES 官方文档搜了一下这个异常的含义。其实这个错误也是ES为了给用户作为一个提醒的,提示用户你所用的当前的ES 集群是不是配置有问题,或者是用户当前的ES的集群配置是不是已经不在够维持当前数据量的负载(是不是需要增加配置)。
在官方文档的文章最后 因为中也再次强调了,避免在text文档中使用fielddata,这会导致使用的大量的堆内存。建议使keyword类型,并且打开Docvalues进行加载到磁盘中进行使用比较合理。
所以当时ES集群中恰好是一台数据节点出现了熔断操作,所以并不会影响到另外两个节点的数据的正常返回。所以线上出现这种问题。所以 circuit_breaking_exception不会中断其他节点的查询结果,但是会中断发生异常的节点的查询结果。当发生circuit_breaking_exception异常时,节点会拒绝执行进一步的搜索或聚合请求,并向客户端返回错误响应。其他节点将继续处理查询请求,并返回它们自己的查询结果,而不会受到发生异常的节点的影响。
同时补充下ES 各个节点的类型以及作用:
在 Elasticsearch 集群中,不同节点类型的作用如下:
- 主节点 (Master
Node):主节点负责管理集群状态,并协调所有的变更操作。主节点通常不参与数据存储和搜索操作,只需要专注于协调管理工作。主节点的数量一般建议不超过3个,以确保在主节点发生故障时能够选举出新的主节点。 - 数据节点 (Data
Node):数据节点负责存储索引数据和执行搜索操作。在大型集群中,通常会有多个数据节点以提高搜索和写入的性能。数据节点也可以与主节点合并成一个节点。 - 客户端节点 (Client
Node):客户端节点不存储数据,仅用于路由搜索请求到数据节点,以避免搜索流量过度集中在数据节点上。客户端节点可以提高搜索性能,并且可以通过在客户端节点上运行负载均衡器来均衡负载。 - 协调节点 (Coordinating
Node):协调节点负责协调搜索请求的路由,并将搜索结果从数据节点合并到一个单一的响应中。协调节点通常用于负载均衡,以避免搜索流量过度集中在单个数据节点上。
注意:以上节点类型不是互相独立的,同一个节点可以同时承担多种角色。
总结
提示:这里对文章进行总结:
文章来源:https://www.toymoban.com/news/detail-772882.html
以上就是今天要讲的内容,相信如果你看到了我这篇文章其实你已经搜索到了关于此类问题的很多解决方法,本文仅仅是对于当前问题的一个的思考以及衍生以及通过官方的角度来解决问题,想要临时解决,可以使用一下办法文章来源地址https://www.toymoban.com/news/detail-772882.html
- 临时增大当前节点的堆内存大小(最直接的)
- 清除缓存fielddata(可能导致该索引的查询变慢,引起业务抖动,提前和客户说明风险)执行下列语句)
-
json POST /索引名/_cache/clear?fielddata=true
(无法根除)
-
- 如果你的机器配置还是可以承受当前数据载体
- 在 elasticsearch.yml中配置:
- 参数: indices.breaker.fielddata.limit= 这个 fielddata 断路器限制fielddata的大小默认为堆内存的60%
- 参数: indices.breaker.request.limit= 这个 request 断路器估算完成查询的其他部分要求的结构的大小默认为堆内存的40%
- 参数: indices.breaker.total.limit=这个 total 断路器封装了 request 和 fielddata 断路器去确保默认情况下这2个部分使用的总内存不超过堆大小的70%。
- 参数: indices.fielddata.cache.size=配置fieldData的Cache大小,可以配百分比也可以配一个准确的数值。cache到达约定的内存大小时会自动清理,驱逐一部分FieldData数据以便容纳新数据。默认值为unbounded无限
- indices.fielddata.cache.expire=用于约定多久没有访问到的数据会被驱逐,默认值为-1,即无限。expire配置不推荐使用,按时间驱逐数据会大量消耗性能。而且这个设置在不久之后的版本中将会废弃。(目前了解到高版本以及取消了当前配置【ES官方也可能在随着磁盘和硬件的效率提示和算法优化逐渐剔除掉fieldData】因为在目前满大街的SSD的配置加持下硬盘速度也是很快的)
到了这里,关于【Elasticsearch】Elasticsearch中使用_id排序导致 data too large 问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!