Online Unsafe Recovery 使用文档
当多数副本的永久性损坏造成部分数据不可读写时,可以使用 Online Unsafe Recovery 功能进行数据有损恢复,使 TiKV 正常提供服务。
功能说明
在 TiDB 中,根据用户定义的多种副本规则,一份数据可能会同时存储在多个节点中,从而保证在单个或少数节点暂时离线或损坏时,读写数据不受任何影响。但是,当一个 Region 的多数或全部副本在短时间内全部下线时,该 Region 会处于暂不可用的状态,无法进行读写操作。
如果一段数据的多数副本发生了永久性损坏(如磁盘损坏)等问题,从而导致节点无法上线时,此段数据会一直保持暂不可用的状态。这时,如果用户希望集群恢复正常使用,在用户能够容忍数据回退或数据丢失的前提下,用户理论上可以通过手动移除不可用副本的方式,使 Region 重新形成多数派,进而让上层业务可以写入和读取(可能是 stale 的,或者为空)这一段数据分片。
在这个情况下,当存有可容忍丢失的数据的部分节点受到永久性损坏时,用户可以通过使用 Online Unsafe Recovery,快速简单地进行有损恢复。使用 Online Unsafe Recovery 时,PD 会自动暂停调度(包括 split 和 merge),然后收集全部节点内的数据分片元信息,用 PD 的全局视角生成一份更实时、更完整的恢复计划后,将其计划下发给各个存活的节点,使各节点执行数据恢复任务。另外,下发恢复计划后,PD 还会定期查看恢复进度,并在必要时重新向各节点分发恢复计划。
适用场景
Online Unsafe Recovery 功能适用于以下场景:
- 部分节点受到永久性损坏,导致节点无法重启,造成业务端的部分数据不可读、不可写。
- 可以容忍数据丢失,希望受影响的数据恢复读写。
使用步骤
前提条件
在使用 Online Unsafe Recovery 功能进行数据有损恢复前,请确认以下事项:
- 离线节点导致部分数据确实不可用。
- 离线节点确实无法自动恢复或重启。
第 1 步:指定无法恢复的节点
使用 PD Control 执行 unsafe remove-failed-stores <store_id>[,<store_id>,...]
命令,指定已确定无法恢复的所有 TiKV 节点,并用逗号隔开,以触发自动恢复。
pd-ctl -u <pd_addr> unsafe remove-failed-stores <store_id1,store_id2,...>
命令输出 Success
表示向 PD 注册任务成功。但仅表示请求已被接受,并不代表恢复成功。恢复任务在后台进行,具体进度使用 show
查看。命令输出 Failed
表示注册任务失败,可能的错误有:
unsafe recovery is running
:已经有正在进行的恢复任务invalid input store x doesn't exist
:指定的 store ID 不存在invalid input store x is up and connected
:指定的 store ID 仍然是健康的状态,不应该进行恢复
可通过 --timeout <seconds>
指定可允许执行恢复的最长时间。若未指定,默认为 5 分钟。当超时后,恢复中断报错。
若 PD 进行过灾难性恢复 pd-recover
操作,丢失了无法恢复的 TiKV 节点的 store 信息,因此无法确定要传的 store ID 时,可指定 --auto-detect
参数允许传入一个空的 store ID 列表。在该模式下,所有未在 PD store 列表中的 store ID 均被认为无法恢复,进行移除。
第 2 步:查看进度等待结束
节点移除命令运行成功后,使用 PD Control 执行 unsafe remove-failed-stores show
命令,查看移除进度。
pd-ctl -u <pd_addr> unsafe remove-failed-stores show
恢复过程有多个可能的阶段:
collect report
:初始阶段,第一次接收 TiKV 的报告获得的全局信息。tombstone tiflash learner
:在不健康的 Region 中,删除比其他健康 Peer 要新的 TiFlash learner,防止极端情况造成 panic。force leader for commit merge
:特殊阶段。在有未完成的 commit merge 时出现,优先对有 commit merge 的 Region 进行force leader
,防止极端情况。force leader
:强制不健康的 Region 在剩余的健康 Peer 中指定一个成为 Raft leader。demote failed voter
:将 Region 不健康的 Voter 降级为 Learner,之后 Region 就可以正常地选出 Raft leader。create empty region
:创建一个空 Region 来补足 key range 的空洞,主要针对的是某些 Region 的所有副本所在的 Store 都损坏了。
每一阶段按照 JSON 格式输出,包括信息,时间,以及具体的恢复计划。例如:
[
{
"info": "Unsafe recovery enters collect report stage",
"time": "......",
"details" : [
"failed stores 4, 5, 6",
]
},
{
"info": "Unsafe recovery enters force leader stage",
"time": "......",
"actions": {
"store 1": [
"force leader on regions: 1001, 1002"
],
"store 2": [
"force leader on regions: 1003"
]
}
},
{
"info": "Unsafe recovery enters demote failed voter stage",
"time": "......",
"actions": {
"store 1": [
"region 1001 demotes peers { id:101 store_id:4 }, { id:102 store_id:5 }",
"region 1002 demotes peers { id:103 store_id:5 }, { id:104 store_id:6 }",
],
"store 2": [
"region 1003 demotes peers { id:105 store_id:4 }, { id:106 store_id:6 }",
]
}
},
{
"info": "Collecting reports from alive stores(1/3)",
"time": "......",
"details": [
"Stores that have not dispatched plan: ",
"Stores that have reported to PD: 4",
"Stores that have not reported to PD: 5, 6",
]
}
]
PD 下发恢复计划后,会等待 TiKV 上报执行的结果。如上述输出中最后一阶段的 Collecting reports from alive stores
显示 PD 下发恢复计划和接受 TiKV 报告的具体状态。
整个恢复过程包括多个阶段,可能存在某一阶段的多次重试。一般情况下,预计时间为 3~10 个 store heartbeat 周期(一个 store heartbeat 默认为 10s)。当恢复完成后,命令执行结果最后一阶段显示 "Unsafe recovery finished"
,以及受影响的 Region 所属的 table id(若无或使用 RawKV 则不显示)和受影响的 SQL 元数据 Region。如:
{
"info": "Unsafe recovery finished",
"time": "......",
"details": [
"Affected table ids: 64, 27",
"Affected meta regions: 1001",
]
}
得到受影响的 table id 后,可以使用 INFORMATION_SCHEMA.TABLES
来查看受影响的表名。
SELECT TABLE_SCHEMA, TABLE_NAME, TIDB_TABLE_ID FROM INFORMATION_SCHEMA.TABLES WHERE TIDB_TABLE_ID IN (64, 27);
若执行过程中发生错误,最后一阶段会显示 "Unsafe recovery failed"
以及具体错误。如:
{
"info": "Unsafe recovery failed: <error>",
"time": "......"
}
第 3 步:检查数据索引一致性(RawKV 不需要)
执行完成后,数据和索引可能会不一致。请使用 ADMIN CHECK
对受影响的表进行数据索引的一致性检查。
ADMIN CHECK TABLE table_name;
若结果有不一致的索引,可以通过重命名旧索引、创建新索引、删除旧索引的方式来修复数据索引不一致的问题。
重命名旧索引:
ALTER TABLE table_name RENAME INDEX index_name TO index_name_lame_duck;
创建新索引:
ALTER TABLE table_name ADD INDEX index_name (column_name);
删除旧索引:
ALTER TABLE table_name DROP INDEX index_name_lame_duck;
第 4 步:移除无法恢复的节点(可选)
- 通过 TiUP 部署的节点
- 通过 TiDB Operator 部署的节点
缩容无法恢复的节点:
tiup cluster scale-in <cluster-name> -N <host> --force清理 Tombstone 节点:
tiup cluster prune <cluster-name>
删除该
PersistentVolumeClaim
。kubectl delete -n ${namespace} pvc ${pvc_name} --wait=false删除 TiKV Pod,并等待新创建的 TiKV Pod 加入集群。
kubectl delete -n ${namespace} pod ${pod_name}