参考资料
参考资料

ES 的填坑经验。主要是关于集群恢复过程中,数据量过大,使用的是普通盘1T,最大的index 大概在50多g ,分片恢复并发数默认是2个,同时2个50g 分片同时恢复,IO吃不消,导致集群 hang 住的问题。

场景描述

线上ES 5台 8核24g 普通盘(事实证明,ES 还是要用ssd,不然数据量在大点就抗不住),通用型云主机。某台主机挂了,开始恢复,CPU会越来越大。最后整个集群卡死。

基本概念

ES 线程池(thread pool)

ES 中每个节点有多种线程池,各有用途。重要的有:

generic :通用线程池,后台的 node discovery,上述的分片恢复(node recovery)等等一些通用后台的操作都会用到该线程池。该线程池线程数量默认为配置的处理器数量(processors)* 4,最小128,最大512。
index :index/delete 等索引操作会用到该线程池,包括自动创建索引等。默认线程数量为配置的处理器数量,默认队列大小:200.
search :查询请求处理线程池。默认线程数量:int((# of available_processors * 3) / 2) + 1,默认队列大小:1000.
get :get 请求处理线程池。默认线程数量为配置的处理器数量,默认队列大小:1000.
write :单个文档的 index/delete/update 以及 bulk 请求处理线程。默认线程数量为配置的处理器数量,默认队列大小:200,在写多的日志场景我们一般会将队列调大。
还有其它线程池,例如备份回档(snapshot)、analyze、refresh 等,这里就不一一介绍了。

详细可参考官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-threadpool.html

shard&replica机制再次梳理3

1
2
3
4
5
6
7
8
 (1)index包含多个shard
(2)每个shard都是一个最小工作单元,承载部分数据,lucene实例,完整的建立索引和处理请求的能力
(3)增减节点时,shard会自动在nodes中负载均衡
(4)primary shard和replica shard,每个document肯定只存在于某一个primary shard以及其对应的replica shard中,不可能存在于多个primary shard
(5)replica shard是primary shard的副本,负责容错,以及承担读请求负载
(6)primary shard的数量在创建索引的时候就固定了,replica shard的数量可以随时修改
(7)primary shard的默认数量是5,replica默认是1,默认有10个shard,5个primary shard,5个replica shard
(8)primary shard不能和自己的replica shard放在同一个节点上(否则节点宕机,primary shard和副本都丢失,起不到容错的作用),但是可以和其他primary shard的replica shard放在同一个节点上

集群恢复之分片恢复

我们知道 ES 集群状态分为三种,green、yellow、red。green 状态表示所有分片包括主副本均正常被分配;yellow 状态表示所有主分片已分配,但是有部分副本分片未分配;red 表示有部分主分片未分配。

一般当集群中某个节点因故障失联或者重启之后,如果集群索引有副本的场景,集群将进入分片恢复阶段(recovery)。此时一般是 master 节点发起更新集群元数据任务,分片的分配策略由 master 决定,
各节点收到集群元数据更新请求,检查分片状态并触发分片恢复流程,根据分片数据所在的位置,有多种恢复的方式,主要有以下几种:

EXISTING_STORE : 数据在节点本地存在,从本地节点恢复。
PEER :本地数据不可用或不存在,从远端节点(源分片,一般是主分片)恢复。
SNAPSHOT : 数据从备份仓库恢复。
LOCAL_SHARDS : 分片合并(缩容)场景,从本地别的分片恢复。
PEER 场景分片恢复并发数主要由如下参数控制:

cluster.routing.allocation.node_concurrent_incoming_recoveries :节点上最大接受的分片恢复并发数。一般指分片从其它节点恢复至本节点。
cluster.routing.allocation.node_concurrent_outgoing_recoveries :节点上最大发送的分片恢复并发数。一般指分片从本节点恢复至其它节点。
cluster.routing.allocation.node_concurrent_recoveries :该参数同时设置上述接受发送分片恢复并发数为相同的值。

详细参数可参考官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/shards-allocation.html
集群卡住的主要原因就是从远端节点恢复(PEER)的并发数过多,导致 generic 线程池被用完。涉及目标节点(target)和源节点(source)的恢复交互流程,后面分析问题时我们再来详细讨论。

解决问题

6.x 版本之后引入了 seqNo,恢复会涉及到 seqNo+translog,这也是6.x提升恢复速度的一大改进。我们重点关注流程中第 2、4、5、7、10、12 步骤中的远程调用,他们的作用分别是:

第2步:分片分配的目标节点向源节点(一般是主分片)发起分片恢复请求,携带起始 seqNo 和 syncId。
第4步:发送数据文件信息,告知目标节点待接收的文件清单。
第5步:发送 diff 数据文件给目标节点。
第7步:源节点发送 prepare translog 请求给目标节点,等目标节点打开 shard level 引擎,准备接受 translog。
第10步:源节点发送指定范围的 translog 快照给目标节点。
第12步:结束恢复流程。

我们可以看到除第5步发送数据文件外,多次远程交互 submitRequest 都会调用 txGet,这个调用底层用的是基于 AQS 改造过的 sync 对象,是一个同步调用。 如果一端 generic 线程池被这些请求打满,发出的请求等待对端返回,而发出的这些请求由于对端 generic 线程池同样的原因被打满,只能 pending 在队列中,这样两边的线程池都满了而且相互等待

修改默同步分片数为1,只能让他慢慢恢复了。

1
2
3
4
5
6
7
curl -X PUT "http://10.19.174.22:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"persistent": {
"cluster.routing.allocation.node_concurrent_recoveries": 1,
"indices.recovery.max_bytes_per_sec": "60mb"
}
}
  • cluster.routing.allocation.node_concurrent_recoveries

设置在节点中最大允许同时进行分片分布的个数,默认为2