测试环境集群压测(esperf)

106 阅读6分钟

  1. 目的

复现12.21号出现的大index查询导致磁盘IO异常。

在yum2环境压测未能复现,现在在测试环境进行测试。

节点信息:

索引信息:




  1. 压测方案

使用esperf 工具实现并发压测。

上次压测未能实现预期效果,怀疑是压测时使用相同的请求参数,导致第1次请求的结果缓存在内存中,后续N次请求直接从内存中查询数据导致的。

本次动态修改请求参数。esperf 脚本支持传入动态参数占位符,然后从文本中抽取参数替换占位符中的变量当作实际参数。

echo '{"from": 100, "size": 1, "timeout": "60s", "post_filter": {"bool": {"filter": [{"geo_shape": {"theGeom": {"shape": {"type": "Point", "coordinates": [$RDICT]}, "relation": "intersects"}, "ignore_unmapped": false, "boost": 1.0}}, {"terms": {"brand": ["KFC"], "boost": 1.0}}], "adjust_pure_negative": true, "boost": 1.0}}, "_source": {"includes": ["id", "brand", "storeCode", "name", "areaType", "status", "updateTime"], "excludes": []}, "sort": [{"name": {"order": "asc"}}, {"storeCode": {"order": "asc"}}]}' | ./esperf -r 50 -t 1 -d ./location.txt "http://172.17.18.254:9200/emap_area_kfc_wgb_20250105_0120/_search"
说明:
-d  <dictionary_file>: Newline delimited strings dictionary file

压测过程中观察 grafana 监控,查看被压测的集群的磁盘io压力状态:

infra-grafana.hwwt2.com/d/na_nxrE_m…

  1. 前置条件

环境复原

索引副本改为1 (现状为5)

Y2 的磁盘改为50GB (现状为200GB)

  1. 使用esperf 工具动态修改请求参数

  2. 压测状态和结果记录1

压测索引 emap_area_kfc_wgb_20250106_0120 单分片,数据量2.2gb

Es jvm参数 -xms3910 -xmx3910

压测效果如下:

压测时刻的磁盘io:Cluster-read=203io/s cluster-write=1.57io/s

io瞬时、短时间内读压力上升,是因为es将索引数据从磁盘加载到内存中。

压测时刻的CPU负载:98%

此时在内存中执行计算。

查询平均耗时:

在内存中执行计算,响应查询速度肯定很快。

esperf 压测平均响应速度维持在0.008s(8毫秒)

(初次压测忘记截图,补充 esperf 压测截图)

  1. 压测状态和结果记录2

对es的某个索引进行压测时,es把该索引数据从磁盘加载到内存中,所以在grafana监控面板上看到io瞬时、短时间内读压力上升。因为es jvm参数设置为 -xms3910 -xmx3910,并且该索引数据量为2.2gb,所以理论上都可以加载到内存中。

当该索引数据从磁盘加载到内存中后,磁盘负载降低,后续的查询是在内存中计算的,此时表现为CPU压力上升。

所以为了检测内存驱逐(内存不足)情况下的查询效率,需要增加压测场景:

对多个大索引同步压测,此时所有索引数据不足以全加载到内存中,会有内存驱逐,此时会服务器不停的执行以下过程:

加载数据到内存 -> 内存分析计算-> 内存驱逐 -> 加载数据到内存 -> 内存分析计算-> 内存驱逐....

此场景更能反映真实的生产环境。

预测: 此时磁盘io 读压力达到高峰后会持续一段时间,CPU负载维持在高位,内存占用维持在高位并重复驱逐动作。

实际:

磁盘io 读压力达到高峰后并没有持续一段时间。(不符合预期,估计是样本参数问题,导致所有的内存数据未加载到内存中)

调整样本参数后,iops达到新高,但还是未能实现预期。

压测时间内CPU负载维持在高位。

内存占用维持在高位并重复驱逐动作。

iops达到新高,未持续一段时间?

怀疑es加载数据使用的不是堆内内存,es在进行jvm内存回收时是不会导致索引数据失效的。

es加载数据使用的是堆外内存,使用free -m 命令查看缓存清除前后的区别:

上图中free=4032mb。

当使用echo 3 > /proc/sys/vm/drop_caches 命令释放缓存后,free=6194mb,相当于回收了2gb多的内存(与emap_area_kfc_wgb_20250107_0120 索引的数据量吻合)

iops达到新高,持续一段时间

如下,构造出多个大索引数据,对这些索引进行压测,模拟内存驱逐场景。

emap_area_kfc_wgb_20250101_0120 ~ emap_area_kfc_wgb_20250110_0120 共10个索引,并行进行压测。

在极限压测下,出现请求失败的情况。

此时测得的磁盘iops如下,极限iops压力 885 io/s:




  1. 结论

经过多种测试场景验证,得知磁盘IOPS升高确实是内存驱逐引起的。性能瓶颈不是在iops,而是内存。

如下以free -m 命令为例:

当服务器内存不足(表现为free 内存不足)而es需要查询其他索引需要加载更多数据时,es已加载进内存的原缓存数据会被驱逐(用于腾出内存空间以加载新索引数据),此时就会导致磁盘IO升高。

如果业务场景上在频繁的使用某些es,并且这些es数据量大于内存空间,就会导致服务器重复执行以下步骤:

图片.png




  1. 建议

基于实际场景分析:

  • 假如实际使用中es是6分片,并且不能修改分片数量或es集群不方便进行rebalance,则新增加的节点并不能均匀分摊压力,此时建议对每个节点增加硬件资源。

  • 如果实际使用中可以对es进行rebalance(或者每天都是新索引,新索引可以设置新的分片数),则增加集群节点数或增加单个节点的资源均可。

另外:

  • 对于es上的数据,可以计算活动期间的活跃索引数据量,计算一下这些索引的数据量,然后对比集群所有节点的 filesystem cache(可以理解为free -m查询出的free内存),如果 filesystem cache >= 活跃索引数据量,就不会引起内存驱逐。比如我们活动期间使用到 6个索引,这些索引总数据量大概20gb(不算副本),则我们集群所有节点可用的filesystem cache 最好大于20gb。(一些轻微的数据倾斜引起的内存驱逐引/io压力不会太高)
  • 可以在活动期间禁止其他索引的访问,尽可能的给活跃索引腾出足够的filesystem cache(比如暂时close索引,或者客户端使用索引别名访问,活动期间临时修改索引别名)。

注:对于es独占型服务器,filesystem cache = (osMamory - es jvmHeap) * 0.8(因为系统内核会占用部分资源)

如何规划es服务的jvm参数和filesystem cache?

es的jvm虚拟机负责处理客户端的请求,它处理数据时会从filesystem cache取数据,如果数据在filesystem cache中不存在则会从磁盘加载。

如果jvm参数设置的太小,则es服务端并行处理多个索引请求时,会引起频繁的gc,影响响应速度。

如果jvm参数设置的太大,则留给filesystem cache的内存不足,可能触发内存驱逐。

建议es jvm xmx设置和filesystem cache 差不多。比如服务器32GB内存,一般内核程序等会占用2gb左右的内存空间,剩余的30gb,设置 jvm xmx15gb。