内容简介:问题:某日下午正开心的逛着超市,突然收到线上es机器的fgc电话告警,随之而来的是一波es reject execution,该es机器所处集群出现流量抖动。排查:
问题:
某日下午正开心的逛着超市,突然收到线上es机器的fgc电话告警,随之而来的是一波es reject execution,该es机器所处集群出现流量抖动。
排查:
回到家打开监控页面,内存占用率有明显的上升,通常服务端不会无端的爆内存,首先排查可能性最高的:读写流量变化。
通过监控页发现入口流量并没有明显抖动,考虑到集群中的不同索引以及不同查询类型,总的入口流量可能会掩盖一些问题,所以继续查看各索引的分操作流量监控,发现索引 A 的scroll流量在故障发生时存在明显的波动,从正常的 10qps 以内涨到最高 100qps 左右,这对于普通查询来说并不高,看来是 scroll 查询有些异样。
起因1:
先说结论:scroll 查询相对普通查询占用的内存开销大很多,考虑到遍历数据的场景,安全的量是控制在 10qps 左右。
相比于普通query,scroll 查询需要后端保留遍历请求的上下文,具体的就是当有init scroll请求到达时,当时的 index searcher 会持有全部索引段的句柄直至scroll请求结束,如果处理不当,比如段缓存等,容易在server端占用大量内存;另外, scroll 查询还需要在server端保存请求上下文,比如翻页深度、scroll context等,也会占用内存资源。
在后续的测试中,客户端单线程使用scroll查询遍历百万级别的索引数据,server端的CPU占用率高达70%左右,观察进程的CPU占用,发现大部分的CPU时间都耗在gc上,这使得server没有足够的CPU时间调度其他任务,导致正常的读写请求不能被及时响应。
# 压测机器配置:1c2g x 10
# 索引配置:5 number_of_shards x 1 number_of_replica,共计约180万数据
起因2:
继续排查scroll执行的查询内容,发现的主要有两种类型。
其一:
{
"query": {"bool":{"must":[{"terms":[11,22,…2003]}]}},
"size":200
}
# terms子句中包含200个id
上面的示例query省略了其他一些过滤条件,白话一下这个查询的含义:
从索引中查询id字段值为数组所包含的200条记录
可以看到的几个特征是:
-
没有filter子句,terms条件在must子句
-
这个查询最多返回200条记录,一次查询就可以得到全部数据
其二:
{
"query": {"bool":{"must":[
{"range":{"create_time":{"gt":0, "lte":604800}}},
{"term":{"shop_id":1}}
]}},
"size":200
}
# range条件包含的数据大约为1000条
# 全索引包含的数据大约1000万条
# create_time不固定,但是区间固定在1周
这里也省略了一些其他干扰条件,只保留最重要的,白话过来的含义:
从1000万全量索引中查询shop_id=1并且create_time在符合条件区间内的数据,
条件区间每10秒变更一次,也就是每10秒查询一次当前时刻之前1周的新数据.
可以得出的几个结论:
-
size为200,要访问全部数据至少需要5次查询
-
create_time的变更很小,类似于 (0, 603800] => (5, 604805],所以每次查询该子条件命中的记录数变化也都不大,都有几百万条
-
没有filter子句
并没有发现filter或者must_not这样在官方文档中明确标明的filter context条件,但是实际上的filter cache在scroll发生期间单机从 500 MB 左右逐渐升高到 6 GB(配置的filter cache最大空间),理论上说不通,直接从代码里找答案。
跟踪query流程,发现bool子句中不论是must还是filter,最终被rewrite之后没有本质上的区别,判断是否可以进入filter cache的条件是:
-
段内最大文档数是否在阈值范围内(Es的filter缓存以段为单位)
-
查询出现频次是否超过阈值
而在出现频次这个部分,Lucene缓存策略还会有isCostly这样的判断,目的是尽量将高消耗的查询尽可能早的缓存起来,提高查询性能,符合isCostly判断的查询包括 terms 和 range 等查询,只要重复出现2次即会被缓存起来,结合起来分析:
-
terms查询并不需要scroll查询,使用普通查询就能解决需求,使用scroll查询增加了server负载
-
range查询重复次数达到了isCostly阈值,也就是说每次遍历数据都会往filter cache中丢入几百万的缓存value,而且命中率极低(下次scroll查询的range起止条件有细微的变化),加大了server的gc负担
解决:
通过上面的分析,我们可以看到有两个因素的影响导致了server的拒绝响应:
-
大量的scroll并发
-
不当的range请求,具体又可以拆分为:
-
高频次,每10秒一次
-
变化快,每次查询的起止范围都有10秒的后延
-
命中数大,百万级别的命中数
针对上面的几点各个击破就是我们的解决方案:
scroll请求:
-
纠正不当使用的terms+scroll查询,使用普通查询;
-
推荐使用search_after替换scroll请求,虽然在效率上有所降低,但是有两个优势:
-
可以重试,scroll如果重试可能会丢失部分数据
-
资源占用低,在相同的测试环境下,CPU占用率只有10%左右
不当的range请求:
-
高频次:降低请求频率,限制到至少1分钟一次,当然不是根本解决方案,推荐将类似的遍历数据请求改到db或者hbase等介质
-
变化快:粗暴点的解决方案是限制时间单位到小时级别,优雅点的话:
-
将时间条件拆分为粗粒度和细粒度的组合,粗粒度以若干小时为单位,细粒度支撑到分钟或者秒级
-
细粒度条件使用script方式执行,原理是filter cache的frequency是用LinkedHashMap作为key容器的,用来累积查询次数,而key的hash计算,普通query是根据查询的条件和值来作为hash输入的,而script查询是使用当前实例的引用,这样就能避免查询被累积(因为每次的hashcode都不一样)
-
命中数大:通过粗细粒度划分可以降低成本
额外补充:这里是根据Es5.1.2版本得到的结论,
最新版本中script查询的hash计算方式也改为根据脚本的内容和参数来进行了
全文完。
以上所述就是小编给大家介绍的《Es因scroll查询引起的gc问题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- mail - PTR 记录引起的问题
- 记一个 emoji 引起的 RN 网络请求问题
- SQL索引引起低级错误造成的生产问题回顾
- disk io引起golang线程数暴涨的问题
- 基于对象可达性原理解决 Handler 引起内存泄露问题
- 使用双缓存解决 Canvas clearRect 引起的闪屏问题
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
基于内容图像检索技术
周明全 / 清华大学 / 2007-12 / 28.00元
《基于内容图像检索技术》从理论方法研究与实现技术角度,总结归纳了基于内容图像检索(CBIR)技术的研究与进展,并融入了作者多年来的相关研究与应用成果,系统地介绍了CBIR的主要概念、基本原理、典型方法、实用范例以及新动向。《基于内容图像检索技术》共有12章分为五部分:第一部分是概述,分析了CBIR的体系结构、技术现状和发展趋势;第一部分讨论图像特征提取,给出图像低层特征(颜色、形状、纹理、空间关系......一起来看看 《基于内容图像检索技术》 这本书的介绍吧!