elasticsearch读写拒绝问题解析

2024-04-13 09:47:32 浏览数 (2)

一.什么是elasticsearch读写

elasticsearch集群在某些情况下出现索引无法对索引进行查询或写入。客户端会收到elasticsearch返回的拒绝日志信息。

二.elasticsearch读写拒绝场景分析

1.线程池被打满导致任务堆积出现写入拒绝

代码语言:javascript复制
org.elasticsearch.common.util.concurrent.EsRejectedExecutionException:  
rejected execution of org.elasticsearch.common.util.concurrent.TimedRunnable@790ac6ff on 
QueueResizingEsThreadPoolExecutor 
[name = 15xxxxxxxxx132/search, queue capacity =1000, min queue capacity =1000,  max queue capacity =1000, 
frame size =2000, targeted response rate = 1s,  task execution EWMA=6.6ms, adjustment amount =50,  
org.elasticsearch.common.util.concurrent.QueueResizingEsThreadPoolExecutor@69e4b10 [Running, pool size =13, active threads =13, queued tasks =1000, completed tasks =445155395]

1.1 elasticsearch的线程池

在elasticsearch集群中,每个数据节点都会维护多个线程池来管理不同的请求的内存消耗,在线程池中许多请求可以得到保留而非直接丢弃。

在elasticsearch的线程池中有很多线程池,例如:generic,search,search_throttled,analyze,snapshot,write线程池。每个线程池管理不同的请求,当前我们这里遇到的是读写线程池被打满导致出现写入拒绝,所以我们这里重点关注"search thread pool"和"write thread pool"。

search thread pool:搜索线程池,主要用于处理搜索类请求。一般我们可以根据集群的(CPU核数 * 3) / 2) 1

来估算集群search thread pool的队列大小。初始化的queue_size为1000;

write thread pool:写入线程池,主要处理数据的写入,更新,删除等批量请求。该线程池固定队列大小的线程池,queue_size为10000。

1.2 查询拒绝

代码语言:javascript复制
GET _cat/thread_pool/search?s=queue:desc&v

可以通过该API来查看查询线程池历史拒绝的次数。

现象1:在查询场景中,集群部分节点CPU使用率过高,剩余节点CPU使用率较低或空闲。查询时出现查询拒绝。

可能原因:索引分片分布不均匀,导致请求集中于集群个别节点,造成请求堆积,出现查询拒绝。

解决办法:优先排查索引分片分布是否合理,如果前期分片规划不合理导致分片无法均匀分布之各个节点,建议可以通过调整索引副本数的方式来分散查询压力。

代码语言:javascript复制
#修改副本
PUT /indexname/_settings
{
   "number_of_replicas" : 2
}

注意:对索引新增副本后,如果分片体积过大,则对应的初始化副本分片的时间也会较长。

现象2:在查询场景中,集群节点整体负载不高,但出现查询拒绝。

可能原因:集群整体负载不高,说明集群资源还没有达到瓶颈,可能是由于缓存队列过小,查询请求太多,打满了队列造成了查询堆积。或者慢查询请求过多,占用了查询队列,可以通过调整缓存队列大小来观察一下查询拒绝的情况。

解决办法:调整缓存队列大小,让线程池能够缓存更多任务,让CPU来进行处理。

我们可以在elasticsearch的yml配置文件中调整该参数thread_pool.search.queue_size的值,来扩大缓存队列大小。

现象3:在查询场景中,集群负载整体都被打满,出现查询拒绝。

可能原因:由于业务压力请求过大,导致集群资源被打满,集群无法响应超出负载能力的业务请求。

解决办法:根据集群达到瓶颈的指标来进行相应资源的升配与扩容。

1.3 写入拒绝

代码语言:javascript复制
GET _cat/thread_pool/bulk?s=queue:desc&v

可以通过该API来查看写入线程池历史拒绝的次数。

日志中频繁出现thread pool rejected为关键字的日志信息,或"too many request"关键字日志信息,客户端出现写入拒绝。

代码语言:javascript复制
"code":5002,"message":"elastic: Error 429 (Too Many Requests): 
rejected execution of org.elasticsearch.transport.TransportService$7@2eaee51a 
on EsThreadPoolExecutor

原因1:缓存队列过小,队列过小导致,大量请求涌入时,造成线程池被打满,任务堆积。

解决办法:

我们可以在elasticsearch的yml配置文件中调整以下参数来增大缓存队列大小

6.4及以上版本调整该参数thread_pool.write.queue_size

5.6版本调整该参数thread_pool.bulk.queue_size

原因2:索引主分片数前期规划过少,导致集群性能利用不充分。

解决办法:当索引创建完成后就不在支持调整主分片数,所以只能重建索引,重新对索引规划索引主分片数,通过别名对索引进行写入。

有关索引分片规划,可以参考我的另一篇文章:https://cloud.tencent.com/developer/article/2356147

原因3:集群负载过高,导致无法负担当前写入业务请求。

解决办法:根据集群达到瓶颈的指标来进行相应资源的升配与扩容。来提高集群的写入性能。

原因4:小请求过多,导致写入线程池队列被打满。

解决办法:需要将索引的写入方式从单条写入,或小bulk写入,改为大bulk写入。来降低对elasticsearch集群的请求量,从而更好的发挥CPU性能。大bulk写入方式,可以复用ES函数堆栈和网络连接,同事降低降低等待网络回包的时间。

1.4 KNN插件熔断造成写入拒绝

代码语言:javascript复制
[c.a.o.k.i.KNNCircuitBreaker][1661xxxxxxxx40132][KNN]  Exception getting stats: java.util.concurrent.ExecutionException:  ElasticsearchSecurityException[action [cluster:admin/knn_stats_action]  is unauthorized for user [_system]]

现象:启用KNN插件后,频繁报KNN fields is rejected as circuit breaker triggered.错误

原因:用户索引mapping开启了knn.circuit_breaker.triggered :true

该参数默认为FALSE,由于在索引中将knn熔断参数设置为true,导致触发了knn插件的内部熔断限流。造成写入报错。

解决办法:将knn.circuit_breaker.triggered 值设置为false。(该参数为集群维度)

代码语言:javascript复制
PUT _cluster/settings 
{
 "persistent" : {
 "knn.circuit_breaker.triggered":false
 },
 "transient" : {
 "knn.circuit_breaker.triggered":false
 }
}

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞