一.什么是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腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!