<
ElasticSearch查询超时优化
>
上一篇

Spark源码学习笔记(十八)
下一篇

Spark源码学习笔记(十七)
记一次线上ES查询过慢问题

原来我们是每个日期生成一个索引,每个索引差不多一千万数据,需要保留前后差不多二十多天的数据,也就是二十多个索引,因为单个索引比较小,查询还是比较快的

后来我们把二十多个索引合成了一个大的索引,数据量上来了,查询性能也跟着下降了,接口查询就会出现超时中断,查看全链路的超时情况,很多查询都要几十秒钟,这个无法忍受啊,虽然我们的接口里面做了3次ES查询,每次查询获取的数据量少则几百条,多则二千条数据,但是几十秒真的太长了,已经使用了路由功能

于是联系运维的同学帮忙排查问题,查看Zabbix监控,排查了一圈后发现,当我们接口使用高峰期时,threadpool中还是会有大量bulk线程,这个的原因时我们会提前导第二天的历史数据导ES,于是我们错开了这个导历史数据的时间,同时想起我们导完数据会设置副本数量为2,这样子副本copy的同时,也会对查询造成影响,延后了通知服务(通知其他接口后其他项目接口会调我们接口请求ES)

第二天来一看,继续傻眼了,接口还是很多超时问题。又让运维的同学帮忙拉一下es的slow log,我拿着接口中的请求去kibana中做查询,使用profile API查看执行情况,发现有些查询走的不是term query,查看mapping发现原来是long类型,继续改mapping,对于range query使用数值类型会比较合适,把mapping中所有用不到的字段或者一些match query的数值字段都改成了keyword类型。之后做了测试,提升的不是很明显,也就是ms级别的,这对于我们秒级近乎九牛一毛啊。上网看了些资料,上网各种查资料,于是有了新的方案,冷热分离+数据预热处理,把查询少的数据或者不会查的历史数据单独放一个索引,查询频繁的数据放一个索引,然后别名指向这2个索引。数据预热的话,我们从其他同事那里要来了他们的查询参数。每天早上导完数据后,就先调用接口预热数据,然后接口通知他们的服务。

第三天,果然有所收获,接口超时个数已经大大降低,就变成只有十来个接口比较慢。我们的查询使用了filter过滤器,按理应该是会缓存的,调用了query cache的API,发现索引中是有缓存的,虽然缓存只有几百兆,肯定没达到缓存上限,query cahce不会缓存term query的结果,继续使用request cache的API,发现没有走request cache,官方文档也说的很清楚,request_cache需要设置size为0,会缓存聚合之类的结果。继续使用profile API在kibana中实验,无意中发现有些查询总共耗时几百毫秒,但是kibana took却要十秒左右,当时有点怀疑是不是网络传输问题导致的,毕竟每次查几千的数据量,后来想想应该不是,毕竟之前索引数据量小的时没出现这个问题。profile只是查询ES的时间,可这并不包括去fetch数据的时间啊,考虑到这点后,怀疑是去磁盘上fetch数据了,我们的只有机械硬盘,没有SSD。立马联系运维同学,让帮忙看看ES内存及机器内存,ES内存31G,机器内存是64G。网上给的很多方案都是设置成机器的一半内存,但是不要超过32G。那么luence能缓存到内存中的只有不到33G内存的数据。看这没啥问题,但是这个问题可大了,当ES内存接近32G这个临界值时,可能不会使用到指针压缩优化,也就是zero base compress oops,经过查看发现,使用的果然是默认的指针压缩策略。接着让运维同学帮忙调小ES内存到26G,并重启集群。重启集群花了一下午的时间,因为要一台一台重启保证线上服务不中断。调小后,ES仍然正常运行。

第四天,接口超时问题已经基本解决,接口查询时间也都控制在了10s内。但是怕哪天性能出现问题,又出现超时,和运维同学商量了下,决定继续缩小ES内存到24G。

第五天,和预想的差不多,接口查询差不多都在5s左右了。果然给了luence更多的内存缓存数据很棒。到5s左右,我们也就不怕了。优化到这里就差不多结束了。毕竟超过查询3s的接口只有十来个。

需要注意的是调小ES内存一定要和运维同学商量,毕竟线上的ES,同时还要查看多天的ES jvm-use情况,确保稳定的情况下再逐步缩小ES内存。

Top
Foot