您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
ES缓存配置及优化方向实践
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
ES缓存配置及优化方向实践
自猿其说Tech
2021-09-16
IP归属:未知
36160浏览
Es6
计算机编程
### 1 简介 Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。 以上其实是一段摘自百度百科的ES的介绍,在我司中使用ES的场景随处可见,而往往我们只是对照着最佳实践或者CF上一段写好的接入文档,照搬接入,对于各项参数等基本无从下手,更多的配置我们始终没有上手机会。最近遇到一个报错,深究之后有了一点自己的心得。 ### 2 背景 最近开发中出现一些ES报错如下: <span style="color:#999999;">Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [fielddata] Data too large, data for [searchWord] would be larger than limit of [2566520832/2.3gb]</span> 测试发现,很多查询都是这样的报错,后来查询到是由于ES默认的缓存设置让缓存区只进不出引起。 那么ES的缓存是怎么存储的呢?是怎么样的运行机制呢? ### 3 ES缓存机制 ES在查询时,会将索引数据缓存在内存(JVM)中,如下图所示 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/e096e0f4-dc67-4112-b82e-6bfde53c468920210916134718.png)</center> 通过上图可以看到,ES的两条界限:驱逐界限和断路器。 当缓存数据到达驱逐界限时,会自动驱逐掉部分数据,把缓存保持在安全的范围内。 当用户准备执行某个查询操作时,断路器就起作用了, 如果当缓存数据+当前查询需要缓存的数据量>断路器限制时,会返回Data too large错误,阻止用户进行这个查询操作。在ES中,ES把缓存数据分成两类,FieldData和其他数据,接下来我们详细看看FieldData,它是造成我们这次异常的根因。 #### 3.1 什么是fielddata 字段数据(fielddata),在 Lucene 中又叫 uninverted index。周所周知,搜索引擎会使用倒排索引(inverted index)来映射单词到文档的 ID 号。而同时,为了提供对文档内容的聚合,Lucene 还可以在运行时将每个字段的单词以字典序排成另一个 uninverted index,可以大大加速计算性能。fielddata 就是作为加速性能的一种方式。 #### 3.2 fielddata核心原理 既然fielddata 作为一个加速性能的方式,当然是被全部加载在内存的时候最为有效。因此Elasticsearch 加载内存 fielddata 的默认行为是 延迟 加载 。 当 Elasticsearch 第一次查询某个字段时,它将会完整加载这个字段所有 Segment 中的倒排索引到内存中,以便于以后的查询能够获取更好的性能。相当于进行了数据缓存,提升查询效率。是一个典型的空间换时间的例子。 fielddata配置参数: ```xml indices.fielddata.cache.size: ``` <span style="color:#E53333;">#节点用于 fielddata 的最大内存,如果 fielddata 达到该阈值,就会把旧数据交换出去。该参数可以设置百分比或者绝对值。默认设置是不限制,所以强烈建议设置该值,比如 10%</span> ```xml indices.fielddata.cache.expire ``` <span style="color:#E53333;">#进入 fielddata 内存中的数据多久自动过期。注意,因为 ES 的 fielddata 本身是一种数据结构,而不是简单的缓存,所以过期删除 fielddata 是一个非常消耗资源的操作。ES 官方在文档中特意说明,这个参数绝对绝对不要设置!</span> #### 3.3 fielddata内存限制 默认情况下,fielddata 设置都是 unbounded ,Elasticsearch 永远都不会从 fielddata 中回收数据。这个默认设置是刻意选择的:fielddata 不是临时缓存。它是驻留内存里的数据结构,必须可以快速执行访问,而且构建它的代价十分高昂。如果每个请求都重载数据,性能会十分糟糕。随着时间的推移,fielddata把堆空间用完,没法再给新的查询数据分配内存,就会导致内存溢出了。Data too large异常就是由于fielddata.cache的默认值为unbounded导致的。为了防止这种情况,Elasticsearch 在 total,fielddata,request 三个层面上都设计有 circuit breaker 以保护进程不至于发生 OOM 事件 比如fielddata层面 ```xml indices.breaker.fielddata.limit ``` <span style="color:#E53333;">#limit默认是 JVM 堆内存大小的 60%。注意,为了让设置正常发挥作用,如果之前设置过 indices.fielddata.cache.size 的,一定要确保 indices.breaker.fielddata.limit 的值大于 indices.fielddata.cache.size 的值。否则的话,fielddata 大小一到 limit 阈值就报错,就永远道不了 size 阈值,无法触发对旧数据的交换任务了。</span> 其他层面相关参数 ```xml indices.breaker.request.limit: #request 断路器估算完成查询的其他部分要求的结构的大小, #默认情况下限制它们到堆大小的40% indices.breaker.total.limit: #total 断路器封装了 request 和 fielddata #断路器去确保默认情况下这2个部分使用的总内存不超过堆大小的70%。 ``` #### 3.4 fielddata设置参考 indices.fielddata.cache.size是允许加载缓存大小(驱逐界限),indices.fielddata.breaker.limit是加载上限(断路界限)。设置时可以是百分比或者是内存大小。断路器界限默认是设置堆的 60% 作为 fielddata 大小的上限,indices.breaker.request.limit request 断路器估算需要完成其他请求部分的结构大小,例如创建一个聚合桶,默认限制是堆内存的 40%。indices.breaker.total.limit total 揉合 request 和 fielddata 断路器保证两者组合起来不会使用超过堆内存的 70%。 内存设置通用格式: ```json PUT /_cluster/settings { "persistent" : { "indices.breaker.fielddata.limit" : 60% } } ``` 断路器设置最好是一个相对保守点的值。 fielddata 需要与 request 断路器共享堆内存、索引缓冲内存和过滤器缓存。Lucene 的数据被用来构造索引,以及各种其他临时的数据结构。 正因如此,它默认值非常保守,只有 60% 。过于乐观的设置可能会引起潜在的堆栈溢出(OOM)异常,这会使整个节点宕掉。另一方面,过度保守的值只会返回查询异常,应用程序可以对异常做相应处理。异常比服务器崩溃要好。 比如一个ES集群,JVM总内存2G,indices.fielddata.cache.size配置了1G,indices.fielddata.breaker.limit配置60%(也就是1.2G),那么如果查询数据超过了1G,那么ES是不会报错,ES是会接受该查询请求,但是旧数据将刷新并释放内存以获取新数据。当ES查询数据大于1.2G,那么此时ES就会拒绝此查询请求并导致异常。 因此配置一个合理的内存是比较重要的,那么肯定生活中会有杠精会说,是否可以不限制内存,那么始终不会达到limit上限,那么就不会报错了,只会驱逐旧数据,加载新数据,答案当然是可以默认无限制,但是这样做会导致频繁evict和reload,大量IO性能损耗,以及内存碎片和gc。因此合理的设置才是王道。但单单只是靠着设置能解决问题吗? ### 4 优化 根据以上所述,我们可以通过参数配置来进行初步优化,但是相比较集群庞大的数据量,内存本身是远远不够的。为了解决这个问题,ES 引入了另一个特性,可以对精确索引的字段,指定 fielddata 的存储方式。这个配置项就是:doc_values。 #### 4.1 doc_values doc_values,其实就是在 ES 将数据写入索引的时候,提前生成好 fielddata 内容,并记录到磁盘上。因为 fielddata 数据是顺序读写的,所以即使在磁盘上,通过文件系统层的缓存,也可以获得相当不错的性能。 注:因为 doc_values 是在数据写入时即生成内容,所以,它只能应用在精准索引的字段上,因为索引进程没法知道后续会有什么分词器生成的结果。 在常规的使用场景中doc_values 使用极其频繁,到 Elasticsearch 5.0 以后,这两者的区别被彻底强化成两个不同字段类型:text 和 keyword。 <center>![](//img1.jcloudcs.com/developer.jdcloud.com/4c63a90d-a4f0-4f52-a1a5-3898fc7ce7ce20210916135227.png)</center> 也就是说,以后不太需要在意 fielddata 的问题了。不过依然有少数情况,会需要对分词字段做聚合统计的话,可以在自己接受范围内,开启这个特性: ```json { "mappings": { "my_type": { "properties": { "message": { "type": "text", "fielddata": true, "fielddata_frequency_filter": { "min": 0.1, "max": 1.0, "min_segment_size": 500 } } } } } } ``` 可以看到在上面加了一段 fielddata_frequency_filter 配置,这个配置是 segment 级别的。综上所示:只有这个 segment 里的文档数量超过 500 个,而且含有该字段的文档数量占该 segment 里的文档数量比例超过 10% 时,才加载这个 segment 的 fielddata。 下面是一个对分词字段做聚合的示例: ```json curl -XPOST 'http://localhost:9200/logstash-2016.07.18/logs/_search?pretty&terminate_after=10000&size=0' -d ' { "aggs": { "group": { "terms": { "field": "punct" }, "aggs": { "keyword": { "significant_terms": { "size": 2, "field": "message" }, "aggs": { "hit": { "top_hits": { "_source": { "include": [ "message" ] }, "size":1 } } } } } } } } ``` #### 4.2 参数配置 参数配置优化就是如上3章节所述,在参数上进行合理配置 #### 4.3 代码优化 如上述所说参数默认是60%是一个综合保守数据,那么我们就要思考为什么单个查询需要超过堆内存的 60% 之多?这个的实际情况,则需要结合对应的业务去分析,是因为数据的日益增长导致集群规格的确成了短板还是说因为查询数据语句问题?如果是代码层面,那么就可以对复杂的查询等进行合理的拆分,从而使大查询分化成小查询,前提当然是在业务,产品多方沟通的背景下进行操作。 ### 5 总结 通过这次问题深入了解,其实不难发现,很多我们常用的组件,并非是全部默认就算接入完成,合理化的配置往往才是后期重点的关注内容,否则一个小小参数配置的缺失就有可能造成整个集群性能问题,最终导致一连串的连锁反应。 ##### 参考: https://www.elastic.co/guide/en/elasticsearch/reference/master/modules-fielddata.html ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:客户服务技术部 晏文
原创文章,需联系作者,授权转载
上一篇:浅谈设计模式之命令模式
下一篇:innodb中MVCC实现原理浅析
相关文章
【技术干货】企业级扫描平台EOS关于JS扫描落地与实践!
开发也要防沉迷--IDEA插件教程
京东mPaaS平台之Android组件化系统私有化部署改造实践!
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号