Elasticsearch-06-索引恢复与ILM
3.5 索引恢复流程
在ES中,索引有5中恢复的方式,具体方式和用途如下所示。这里我们主要关注的是Peer Recovery,这也是故障恢复使用的方式
类型 | 描述 |
---|---|
EmptyStoreRecoverySource | 从新的副本中恢复 |
ExistingStoreRecoverySource | 从现有的磁盘存储中恢复 |
PeerRecoverySource | 从主分片中恢复 |
SnapshotRecoverySource | 从快照中恢复 |
LocalShardsRecoverySource | 从同一节点上的其他分片恢复(收缩索引操作) |
ES恢复分片的核心思想:通过Lucene分段和translog来进行恢复
3.5.1 相关概念
1) sequence number
sequence number是用于标记分片中操作的序列号,
2) primary term
primary term是用于标记主分片状态的数据量。它由master统一进行分配,当副本组的主分片发生变化时(通常是提升新副本为主分片),primary term递增。
通过primary term和sequence number,我们就可以很好的标识在分片上进行的操作顺序。
3) checkpoint
有了primary term 和 sequence number 之后,我们就可以检测出副本和主分片的差异,并使他们重新对齐
ES通过维护 global checkpoint 和 local checkpoint 来比较主分片和副本之间的差异。
-
global checkpoint
global checkpoint 是所有副本都已经完成操作的序列号。它由主分片来维护。主分片通过跟踪在副本上完成的操作来实现。当主分片检测到所有副本均已给出大于当前操作的序列号之后,它会向前推进 global checkpoint
-
local checkpoint
local checkpoint 也是一个序列号,低于 local checkpoint 的操作表示均已在本地副本上执行成功(写Lucene和translog成功,不一定刷盘)。当副本向主分片ack一个操作之后,它们也会更新 local checkpoint
通过比较 global checkpoint 和 local checkpoint,这样就只需要比较一小部分的操作,从而提高了检查效率
4)基于文件恢复和基于操作恢复
由于主分片和副本都是独立的Lucene索引,它们会各自执行自己的Lucene段合并。即使它们保存相同的文档和执行相同的操作,它们的文件也不会是一样的。更何况通过文件恢复还需要通过网络传输大量的数据文件。因此,ES更倾向于通过操作来进行恢复
5)恢复状态阶段
阶段 | 描述 |
---|---|
INIT | |
INDEX | 恢复Lucene文件,使用本地文件或从复制一个新的 |
VERIFY_INDEX | 验证索引 |
TRANSLOG | 启动engine,重置translog,建立Lucene索引 |
FINALIZE | |
DONE | 完成 |
3.5.2 恢复流程
![ES数据恢复流程](https://gitee.com/primabrucexu/image/raw/main/20210713174728.png)
3.5.3 核心问题
假设在副本恢复期间一直有写操作,如何保证主分片和副本的数据一致
主要面临的问题有两个:translog会随着flush而删除,以及重做操作的时序
1)保留translog
在ES早期版本中,副本恢复时,主分片会有如下动作
-
phase1
把主分片的Lucene做快照,发给副本。期间不阻塞写操作,新增写操作记录在translog中
-
phase2
把主分片的translog做快照,发给副本重做操作。期间不阻塞写操作,新增写操作同样记录在translog中
-
phase3
给主分片加写锁,把剩余的translog发送给副本。此时数据量很小,所以阻塞时间很短
在ES2中,重构了translog的文件管理模块,允许存在多个translog文件。其维护一个引用文件的列表,包括未完成的recovery,以及那些包含尚未提交到Lucene的operations的文件。
同时提出了translog.view的概念,允许recovery获取一个引用,包括所有当前未提交的translog 文件,以及所有未来新创建的 translog文件,直到view关闭。它们可以使用这个view做operations的遍历操作。在后续版本中,用TranslogDeletionPolicy来translog.view。核心思想不变。
于是现阶段流程如下:
-
phase1
获取translog保留锁,从获取保留锁开始,会保留translog不受其刷盘清空的影响。然后调用Lucene接口把shard做快照,快照含有shard中已经刷到磁盘的文件引用,把这些shard数据复制到副本节点。在phase1结束前,会向副分片节点发送告知对方启动Engine,在phase2开始之前,副分片就可以正常处理写请求了。
-
phase2
对translog做快照,这个快照里包含从phase1开始,到执行translog快照期间的新增索引。将这些translog发送到副分片所在节点进行重放。
2)时序问题
假设在第一阶段执行期间,有客户端索引操作要求将docA的内容写为1,主分片执行了这个操作,而副分片由于尚未就绪所以没有执行。第二阶段期间客户端索引操作要求写 docA 的内容为2,此时副分片已经就绪,先执行将docA写为2的新增请求,然后又收到了从主分片所在节点发送过来的translog重复写docA为1的请求该如何处理?
写操作有三种类型:创建索引、更新、删除。其中创建索引无需考虑时序问题,那么会造成时序问题的操作就是更新和删除。
![image-20210713182639977](https://gitee.com/primabrucexu/image/raw/main/20210713182640.png)
还记得ES的文档中有version这么一个字段吗?这个字段我们能拿来做乐观锁用。同样的原理,ES也采用version字段来标记操作的顺序
ES在执行写操作的时候,通过比较操作所带的版本号和预期的版本号来决定这个操作要不要执行。在恢复过程中,的实现逻辑就是通过比较已有文档版本号和操作的版本号来判断这个操作是否过时
3.5.5 如何提高恢复效率
- 配置项
cluster.routing.allocation.node_concurrent_recoveries
决定了单个节点执行恢复时的最大并发数,默认为2 - 配置项
indices.recovery.max_bytes_per_sec
决定节点间复制数据时的限速,可以适当提高此值或取消限速。但只有phase1可以配置限速 - 配置项
cluster.routing.allocation.node_initial_primaries_recoveries
决定了单个节点执行主分片recovery时的最大并发数,默认为4。由于主分片的恢复不涉及在网络上复制数据,仅在本地磁盘读写,所以在节点配置了多个数据磁盘的情况下,可以适当提高此值 - 在重启集群之前,先暂停写入,手动执行sync flush。这样可以有机会跳过phase1
- 合并 Lucene 分段,对于冷索引甚至不再更新的索引执行_forcemerge,较少的Lucene分段可以提升恢复效率,例如,减少对比,降低文件传输请求数量。
- 适当地多保留些 translog,配置项
index.translog.retention.size
默认最大保留512MB,index.translog.retention.age
默认为不超过12小时。调整这两个配置可让恢复过程有机会跳过phase1。
3.6 索引生命周期管理(ILM)
3.6.1 简介
ES在6.7中正式引入了索引生命周期管理功能,其根据索引的更新和搜索评率,将索引的生命周期划分为5个阶段
- hot —— 索引在非常活跃的更新和查询
- warm —— 索引几乎不再更新,但仍有较多的查询
- cold —— 索引不再更新,并且不经常查询。 信息仍然可以搜索,但查询速度较慢
- frozen —— 索引不再更新,并且很少查询。 信息仍然可以搜索,但查询速度非常慢
- Delete —— 不再需要这个索引和它的数据了
ILM在索引阶段变化时执行如下操作:
- Rollover —— 当索引触发限制条件后,创建新的索引
- Shrink —— 缩减索引的主分片数量
- Force merge —— 强制合并分片中段的数量
- Freeze —— 冻结索引,使其只读
- Delete —— 删除索引
注意
ILM在索引有分片为分配时也会执行更新策略,这可能导致某些不可控的后果。同时,ILM定期运行,检查索引是否符合策略条件,并执行任何需要的步骤。 为避免竞争条件,ILM可能需要多次运行以执行完成操作所需的所有步骤。这可能导致执行时间会比预期的要长
3.6.2 生命周期策略
实例与说明
PUT _ilm/policy/hot-warm-cold-delete-60days
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size":"50gb", // 设置该阶段索引的最大大小
"max_age":"30d" // 设置该阶段索引的最长持续时间
},
"set_priority": {
"priority":50 // 设置恢复优先级
}
}
},
"warm": {
"min_age":"7d", // 设置进入该阶段索引的最小年龄
"actions": {
"forcemerge": { // 段强制合并设置
"max_num_segments":1
},
"shrink": { // 缩减主分片设置
"number_of_shards":1
},
"allocate": { // 再分配节点设置
"require": {
"data": "warm"
},
"number_of_replicas" : 2 // warm阶段中的副本数量
},
"set_priority": {
"priority":25
}
}
}
}
}
}