Elasticsearch 线程池和队列问题,请先看这一篇
• 问题1:从Kafka消费数据导入 elasticsearch 时,批量 bulk 写入抛异常被拒绝。ES 集群四个节点,其中:两个节点node1和node4 thread pool bulk rejected 30多万条数据,es bulk thread pool 线程数8、队列200, Kafka写线程池 thread数2*cores+cores/2、队列数3。目前是想平衡一下写的速度和 es 处理的速度,不过现在还没有可用环境压测,想问有经验数据或方法参考吗?
• 问题2:多套系统使用一套集群,错误日志如下
针对问题 2,初步排查日志,是大量日志写入造成队列满了,造成集群直接拒绝写入了。
问题 2 初步解决方案:修改默认值、扩大队列,根据业务后续持续观察队列大小,不再出现上述情形。
问题 1、2 都会引出:Elasticsearch 线程池和队列知识点。
Elasticsearch 使用线程池(Thread pool )来管理请求并优化集群中每个节点上的资源使用。
主要线程池包括:搜索(search)、获取(get)和写入(write)等。
通过运行以下命令可以看到线程池全貌:
其中:
• name:代表某一种线程池(写入、检索、刷新或其他)。
• type:代表线程数类型。
通过运行上面的命令可以看到每个节点都有许多不同的线程池、线程池的大小和类型,还可以看到哪些节点拒绝了操作。
Elasticsearch根据每个节点中检测到的线程数(number of processors,后面会讲到这个参数)自动配置线程池参数。
固定数量的线程,具有固定的队列大小。
Fixed 类型线程使用示例如下:
可变数量的线程,Elasticsearch会根据工作负载自动调节线程大小(值介于:core 到 max 之间)。
Scaling 类型线程使用示例如下:
• 固定数量的线程,队列大小会动态变化以保持目标响应时间。
• 该功能 8.0+ 版本会废弃,这里也不着重讲解。
fixed_autoqueue_size 类型线程使用示例如下:
强调了队列大小可变。
若要查看哪些线程 CPU 利用率高或花费的时间最长,可以使用以下查询。
该 API 有助于排查性能问题。
更多推荐:深入解读 Elasticsearch 热点线程 hot_threads
值得注意的是,线程池是根据 Elasticsearch 在基础硬件上检测到的线程数(number of processors)设置的。
如果检测失败,则应在 elasticsearch.yml 中显式设置硬件中可用的线程数。
特别是在一台宿主机配置多个 Elasticsearch 节点实例的情况下,若要修改其中一个节点线程池或队列大小,则要考虑配置 processors 参数。
elasticsearch.yml 中设置如下所示:
大多数线程池还具有与之关联的队列,以使 Elasticsearch 可以将请求存储在内存中,同时等待资源变得可用来处理请求。
但是,队列通常具有有限的大小,如果超过该大小,Elasticsearch将拒绝该请求。
有时你可能会增加队列的大小以防止请求被拒绝,但要结合资源实际进行修改,千万别盲目修改。
实际上,如果值设置的非常大,甚至可能适得其反。因为通过设置更大的队列大小,该节点将需要使用更多的内存来存储队列,这就意味着将剩下相对较少的内存来响应和管理实际请求。
此外,增加队列大小还会增加将操作响应保留在队列中的时间长度,从而导致客户端应用程序面临超时问题。
以下的莽撞行为,大家是要实战中避免的。
通常,唯一有需要增加队列大小的情况是:在请求数量激增导致无法在客户端管理此过程且资源使用率并未达到峰值。
你可以借助 Kibana Stack Monitoring 可视化监控指标以更好地了解 Elasticsearch 集群的性能。
Kibana 监控面板中的总视图、节点视图、索引视图如下所示:
上图是:Kibana 7.6 版本中的监控截图,标红的地方是我在批量写入数据。
• search Rate:检索速率
• search Latency:检索延时
• indexing Rate:写入速度
• indexing Latency:写入延时
队列的增加(Growing)表明 Elasticsearch难以满足请求,而拒绝(rejection)则表明队列已经增长到 Elasticsearch 拒绝的程度。
需要检查导致队列增加的根本原因,并尝试通过在客户端减轻相关写入或检索操作来平衡对集群线程池的压力。
• 节点级别配置,而不再支持 5.X 之前的版本动态 setting 修改。
• 重启集群后生效。
类似文章开头问题 2,如果 Elasticsearch 集群开始拒绝索引/写入(index)请求,则可能有多种原因。
通常,这表明一个或多个节点无法跟上索引 / 删除 / 更新 / 批量请求的数量,从而导致在该节点上建立队列且队列逐渐累积。
一旦索引队列超过队列的设置的最大值(如 elasticsearch.yml 定义的值或者默认值),则该节点将开始拒绝索引请求。
排查方法:需要检查线程池的状态,以查明索引拒绝是否总是在同一节点上发生,还是分布在所有节点上。
• 如果 reject 仅发生在特定的数据节点上,那么您可能会遇到负载平衡或分片问题。
• 如果 reject 与高 CPU 利用率相关联,那么通常这是 JVM 垃圾回收的结果,而 JVM 垃圾回收又是由配置或查询相关问题引起的。
• 如果集群上有大量分片,则可能存在过度分片的问题。
• 如果观察到节点上的队列拒绝,但监控发现 CPU 未达到饱和,则磁盘写入速度可能存在问题。
不要妄图快速提高写入速度,一下调很大,很大势必会写入 reject。
首先尝试一次索引 100 个文档,然后索引 200 个,再索引 400 个,依此类推......
当索引写入速度(indexing rate)开始趋于平稳时,便知道已达到数据批量请求的最佳大小。
写入 reject、“429 too many requests” 等都是非常常见的错误,问题多半和线程池和队列大小有关系,需要结合业务场景进行问题排查。
本文“抛砖引玉”,给出线程池和队列相关总结知识。您在实战中遇到的类似问题吗?欢迎留言探讨交流。