TiDB 数据库紧急救援参考手册(数据恢复)

102 阅读14分钟

TiDB 数据库紧急救援参考手册(数据恢复)


文档控制

项目名称
文档名称
1. 文档属性
文档编号
2. 修改记录
日期修改人修改记录备注
3. 审阅记录
日期审阅人公司名称及职务备注

版权声明

平凯星辰(北京)科技有限公司版权所有,保留一切权利。未经本公司许可,任何单位和个人不得擅自摘抄、复制本文档内容,并以任何形式传播。




目录

文档控制2

一、问题确认4

二、使用场景4

三、利用快照读恢复数据5

使用条件5

操作步骤5

四、利用Flashback/Recover 命令秒恢复误删表6

使用条件6

Recover命令7

Recover操作步骤(方法一) 7

Recover操作步骤(方法二) 8

Flashback 命令(适用版本>= v4.0.0) 9

Flashback操作步骤9

五、Dumpling 导出 TiDB 的历史数据11

1、使用条件11

2、dumpling导出11

3、导入 dumpling 备份数据12

六、多数副本丢失数据恢复12

问题背景12

2个副本丢失处理12

3个副本丢失处理17




一、问题确认

  1. 问题发生的时间,集群发生的问题( 比如误删数据,多副本丢失)

  2. 用户期望(如先恢复部分重要数据优先)




二、使用场景

  1. 用户在处理数据的过程中,执行了错误的更新、删除操作。这时在 TiDB 中如何快速恢复误操作前的数据,请参考 【利用 GC 快照读恢复数据】

  2. 如果直接执行了 drop table,或者 truncate table,将整张表瞬间删除,这时如何恢复,请参考【利用 Recover/Flashback 命令秒恢复误删

  3. 如果需要指定时间点导出历史数据到文件,请参考 【Dumpling 导出 TiDB 的历史数据快照

  4. TiKV 虽然已有高可用方案,采用 3 副本的方式冗余数据,但如果特殊意外丢失了 2 副本该如何恢复,请参考【多数副本丢失数据恢复指南

数据恢复场景错误的更新、删除操作**(update,delete)**误删表**(Drop table)**导出历史数据到文件后恢复2副本丢失恢复3副本丢失恢复集群状态
恢复条件恢复 tikv_gc_safe_point 时间之后的数据恢复 tikv_gc_safe_point 时间之后的数据恢复 tikv_gc_safe_point 时间之后的数据存在1副本可以接受数据丢失
工具设置 tidb_snapshot变量Flashback/Recover 命令dumplingtikv-ctlunsafe-recovertikv-ctlrecreate-region
工具版本选择与tidb 相同v4 ,v5 支持 FLASHBACK / RECOVER,v3 仅支持 RECOVER;选择dumpling最新版本与tidb 相同与tidb 相同
恢复影响范围恢复的表恢复的表恢复的表需要停止tikv服务和重启集群需要停止tikv服务和重启集群
按重要性选择恢复数据
是否有损恢复
恢复耗时估计小于1分钟小于1分钟和恢复数据量有关分钟级别分钟级别



三、利用快照读恢复数据

使用条件

只适用于恢复 tikv_gc_safe_point 时间之后的数据,可以使用以下 SQL 语句查询当前的 safePoint,即 GC 已经清理到的时间点:

SELECT * FROM mysql.tidb WHERE variable_name = 'tikv_gc_safe_point';
操作步骤

1、调整 GC 保留时间,如将 GC 调整为保留最近一天以内的数据。

update mysql.tidb set VARIABLE_VALUE="24h" where VARIABLE_NAME="tikv_gc_life_time";

2、设置tidb_snapshot变量为需要恢复的历史时间点,创建一个与待恢复的数据表同结构的临时表

set @@tidb_snapshot="2021-10-08 16:45:26";create table t_20211008 like t;

3、按照业务逻辑将需要的数据插入到临时表:

insert into t_20211008 select * from t where c=2;

4、按照业务逻辑将数据从临时表反更新或插入到原表

5、按照业务逻辑校验数据

6、将 GC 保留时长调整为恢复之前的设置

update mysql.tidb set VARIABLE_VALUE="10m0s" where VARIABLE_NAME="tikv_gc_life_time"
  1. 根据需要删除临时表




四、利用Flashback/Recover 命令秒恢复误删表

使用条件

只要被 DROP 或 TRUNCATE 删除的表是在 tikv_gc_safe_point 时间之后,都能用 FLASHBACK TABLE 语法来恢复。

如果删除了一张表并过了 GC lifetime,就不能再用 FLASHBACK TABLE 语句来恢复被删除的数据了,否则会返回错误,错误类似于 Can't find dropped/truncated table 't' in GC safe point 2020-03-16 16:34:52 +0800 CST。

可以使用系统变量 tidb_gc_life_time 配置数据的历史版本的保留时间(默认值是 10m0s)。可以使用以下 SQL 语句查询当前的 safePoint,即 GC 已经清理到的时间点:

SELECT * FROM mysql.tidb WHERE variable_name = 'tikv_gc_safe_point';

Recover命令

v3 仅支持 RECOVER;

Recover操作步骤(方法一)

1、模拟误删除操作

| MySQL [test]> drop table t2;MySQL [test]> show tables;+----------------+| Tables_in_test |+----------------+| t1 || t3 |+----------------+MySQL [test]> select * from t2 limit 1;ERROR 1146 (42S02): Table 'test.t2' doesn't exist | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

2、执行 recover 操作

| MySQL [test]> recover table t2;MySQL [test]> show tables;+----------------+| Tables_in_test |+----------------+| t1 || t2 || t3 |+----------------+ | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |

  1. 恢复完成,业务确认检查。
Recover操作步骤(方法二)

通过查询 ddl jobs 队列,指定 job id 的方式来进行恢复。

| MySQL [test]> ADMIN SHOW DDL JOBS;+--------+---------+------------+---------------+--------------+-----------+----------+-----------+-----------------------------------+-----------------------------------+--------+| JOB_ID | DB_NAME | TABLE_NAME | JOB_TYPE | SCHEMA_STATE | SCHEMA_ID | TABLE_ID | ROW_COUNT | START_TIME | END_TIME | STATE |+--------+---------+------------+---------------+--------------+-----------+----------+-----------+-----------------------------------+-----------------------------------+--------+| 73 | test | t2 | drop table | none | 1 | 71 | 0 | 2020-03-08 15:38:47.076 +0800 CST | 2020-03-08 15:38:47.276 +0800 CST | synced || 72 | test | t2 | create table | public | 1 | 71 | 0 | 2020-03-08 15:38:37.626 +0800 CST | 2020-03-08 15:38:37.726 +0800 CST | synced || 70 | test | t2 | drop table | none | 1 | 66 | 0 | 2020-03-08 15:38:20.576 +0800 CST | 2020-03-08 15:38:20.776 +0800 CST | synced |+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

从 DDL 记录可以看到,我们一共执行了 2 次 drop table t2 的操作,如果只执行 recover table t2 恢复的是最近一次删除的表。如果我希望恢复前面一次删除的动作,就需要用到下面的命令

MySQL [test]> recover table by job 70;
Flashback 命令(适用版本>= v4.0.0)

可恢复 truncate 误操作。

Flashback操作步骤

1、恢复被 DROP 删除的表数据(例如:t)

FLASHBACK TABLE t;

2、恢复被 TRUNCATE 的表数据

由于被 TRUNCATE 的表还存在,所以需要重命名被恢复的表,否则会报错表 t 已存在。

| MySQL [test]> show tables;+----------------+| Tables_in_test |+----------------+| t1 || t2 || t3 |+----------------+MySQL [test]> truncate table t2;MySQL [test]> flashback table t2 to t4;MySQL [test]> show tables;+----------------+| Tables_in_test |+----------------+| t1 || t2 || t3 || t4 |+----------------+MySQL [test]> select count(1) from t2;+----------+| count(1) |+----------+| 0 |+----------+MySQL [test]> select count(1) from t4;--同上+----------+| count(1) |+----------+| 524288 |+----------+ | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |




五、Dumpling 导出 TiDB 的历史数据

使用 Dumpling 导出 TiDB 的历史数据快照,Dumpling 在导出较大的数据集时有较好的性能。

dumpling可以把存储在 TiDB 中的数据导出为 SQL 或 CSV 格式,用于逻辑全量备份。

1、使用条件

只适用于恢复 tikv_gc_safe_point 时间之后的数据,可以使用以下 SQL 语句查询当前的 safePoint,即 GC 已经清理到的时间点:

SELECT * FROM mysql.tidb WHERE variable_name = 'tikv_gc_safe_point';
2、dumpling导出

设置 --snapshot 为需要导出的历史时间点

/dumpling --snapshot "2021-12-30 14:29:30" -u root -P 4000 -h 172.16.4.123 --filetype sql -t 6 -o ./bak -r 200000 -F 256MiB-B-p
3、导入 dumpling 备份数据



六、多数副本丢失数据恢复

问题背景

TiDB 默认配置为 3 副本,每一个 Region 都会在集群中保存 3 份,它们之间通过 Raft 协议来选举 Leader 并同步数据。Raft 协议可以保证在数量小于副本数(注意,不是节点数)一半的节点挂掉或者隔离的情况下,仍然能够提供服务,并且不丢失任何数据。 对于 3 副本集群,挂掉一个节点除了可能会导致性能有抖动之外,可用性和正确性理论上不会受影响;

  1. 同时挂掉 2 个副本,一些 region 就会不可用,而且如果这 2 个副本无法完整地找回了,还存在永久丢失部分数据的可能。
  2. 同时挂掉3副本及以上,这个 Region 的数据就丢失了,无法恢复。 可以通过创建 1 个空 Region 来解决 Region 不可用的问题。

SQL语句出现region不可用的报错:

select count(*) from t_user;ERROR 9005 (HY000): Region is unavailable
2个副本丢失处理

1、启动 pd-ctl客户端

pd-ctl -i -u http://172.16.134.133:2379

2、检查宕机的两台机器对应的store_id,输入store 指令,找出“state_name”: “Disconnected” 的store id。

store“store”: {“id”: 5,“address”: “172.16.134.136:20160”,“labels”: [{“key”: “host”,“value”: “tikv3”}],“version”: “4.0.0-rc”,“status_address”: “172.16.134.136:20180”,“git_hash”: “f45d0c963df3ee4b1011caf5eb146cacd1fbbad8”,“start_timestamp”: 1594632461,“binary_path”: “/data1/tidb-deploy/tikv-20160/bin/tikv-server”,“last_heartbeat”: 1594700897622993541,“state_name”: “Disconnected”},…

3、通过 pd-ctl config get 获取 region-schedule-limit、replica-schedule-limit、leader-schedule-limit、merge-schedule-limit

» config show“region-schedule-limit”: 2048,“replica-schedule-limit”: 64,“leader-schedule-limit”: 4,“merge-schedule-limit”: 8,

4、通过 pd-ctl config set 将这 4 个参数设为 0

关闭调度主要为将恢复过程中可能的异常情况降到最少,需在故障处理期间禁用相关的调度。

» config set region-schedule-limit 0Success!» config set replica-schedule-limit 0Success!» config set leader-schedule-limit 0Success!» config set merge-schedule-limit 0Success!

5、使用 pd-ctl 检查大于等于一半副本数在故障节点上的 Region,并记录它们的 ID(故障节点为store id 4,5):

| pd-ctl -u -d region --jq=’.regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as totalmap(if.==(4,5)then.elseemptyend)length>=total | map(if .==(4,5) then . else empty end) | length>=total-length) }’ | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

| » region --jq=".regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as totalmap(if.==(4,5)then.elseemptyend)length>=total | map(if .==(4,5) then . else empty end) | length>=total-length) }"{“id”:3080,“peer_stores”:[4,6,5]}{“id”:18,“peer_stores”:[4,5,6]}{“id”:3084,“peer_stores”:[4,6,5]}{“id”:75,“peer_stores”:[4,5,6]}{“id”:34,“peer_stores”:[6,4,5]}{“id”:4005,“peer_stores”:[4,6,5]}{“id”:4009,“peer_stores”:[5,6,4]}{“id”:83,“peer_stores”:[4,5,6]}{“id”:3076,“peer_stores”:[4,5,6]}{“id”:4013,“peer_stores”:[5,4,6]}{“id”:10,“peer_stores”:[4,6,5]}{“id”:26,“peer_stores”:[4,6,5]}{“id”:59,“peer_stores”:[4,5,6]}{“id”:3093,“peer_stores”:[4,5,6]} | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

6、根据region ID,确认region属于哪张表,以备后续同步数据需要

curl http://172.16.134.133:10080/regions/4009{“region_id”: 4009,“start_key”: “dIAAAAAAAABN”,“end_key”: “dIAAAAAAAABNX3KAAAAAAAt8fw==”,“frames”: [{“db_name”: “sbtest2”,“table_name”: “t_user”,“table_id”: 77,“is_record”: true,“record_id”: 752767}]

7、在剩余正常的kv节点上执行停Tikv,以便释放文件锁

tiup cluster stop tidb-test -R tikv

8、在所有健康的节点上执行(操作需要确保健康的节点关闭了Tikv):

./tikv-ctl --data-dir /data1/tidb-data/tikv-20160 unsafe-recover remove-fail-stores -s 4,5 --all-regions

当Region 比较少,则可以在给定 Region 的剩余副本上,移除掉所有位于故障节点上的 Peer,在这些 Region 的未发生掉电故障的机器上运行:

tikv-ctl --db /path/to/tikv-data/db unsafe-recover remove-fail-stores -s <s1,s2> -r <r1,r2,r3>,对于region较多的情况,此操作则较为繁琐。

9、停止PD节点:

tiup cluster stop tidb-test -R pd

10、重启启动PD tikv节点:

tiup cluster start tidb-test -R pd,tikv

11、检查没有处于leader状态的region(要保持没有),这里没有发现没有leader状态的region。

| pd-ctl -i -u http://172.16.134.133:2379» region --jq ‘.regions[]|select(has(“leader”)|not)|{id: .id,peer_stores: [.peers[].store_id]}’» | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

12、重新修改pd参数

pd-ctl -i -u http://172.16.134.133:2379» config set region-schedule-limit 2048Success!» config set replica-schedule-limit 64Success!» config set leader-schedule-limit 4Success!» config set merge-schedule-limit 8Success!

13、检查查询数据是否正常

| select count() from t_user;±---------+| count() |±---------+| 3000000 |±---------+1 row in set (9.95 sec) | | ----------------------------------------------------------------------------------------------------------------- |

恢复完成,业务检查确认数据。

3个副本丢失处理
数据不能恢复,创建空 Region 解决 Unavailable 报错

1、3个tikv节点不可用,报错 Region is unavailable

MySQL [sbtest2]> select count(*) from t_user;ERROR 9005 (HY000): Region is unavailable

2、检查宕机的机器对应的store_id

pd-ctl -i -u http://172.16.134.133:2379» store

3、通过 pd-ctl config get 获取 region-schedule-limit、replica-schedule-limit、leader-schedule-limit、merge-schedule-limit并通过 pd-ctl config set 将这 4 个参数设为 0

config set region-schedule-limit 0Success!» config set replica-schedule-limit 0Success!» config set leader-schedule-limit 0Success!» config set merge-schedule-limit 0Success!

4、使用 pd-ctl 检查大于等于一半副本数在故障节点上的 Region,并记录它们的 ID(故障节点为store id 1,5,6)

| region --jq=".regions[] | {id: .id, peer_stores: [.peers[].store_id] | select(length as totalmap(if.==(1,5,6)then.elseemptyend)length>=total | map(if .==(1,5,6) then . else empty end) | length>=total-length) }"{“id”:3089,“peer_stores”:[5,4,6]}{“id”:47,“peer_stores”:[4,5,6]}{“id”:75,“peer_stores”:[4,5,6]}{“id”:30,“peer_stores”:[6,4,5]}{“id”:135,“peer_stores”:[6,4,5]}{“id”:4017,“peer_stores”:[6,7,5]}{“id”:67,“peer_stores”:[4,5,1]}{“id”:2289,“peer_stores”:[4,6,5]}{“id”:18,“peer_stores”:[6,4,5]}{“id”:39,“peer_stores”:[6,4,5]}{“id”:51,“peer_stores”:[4,6,5]}{“id”:10,“peer_stores”:[4,5,6]}{“id”:14,“peer_stores”:[6,5,4]}{“id”:83,“peer_stores”:[6,4,5]}{“id”:59,“peer_stores”:[6,4,5]}{“id”:6768,“peer_stores”:[1,6,4]}{“id”:22,“peer_stores”:[4,5,6]}{“id”:26,“peer_stores”:[6,4,5]}{“id”:43,“peer_stores”:[6,4,5]}{“id”:131,“peer_stores”:[6,4,5]}{“id”:4009,“peer_stores”:[6,1,5]}{“id”:2,“peer_stores”:[7,6,5]}{“id”:63,“peer_stores”:[4,5,1]}{“id”:87,“peer_stores”:[6,4,5]}{“id”:6734,“peer_stores”:[6,1,5]}{“id”:3080,“peer_stores”:[6,4,5]}{“id”:3084,“peer_stores”:[6,4,5]}{“id”:3076,“peer_stores”:[6,4,5]}{“id”:34,“peer_stores”:[6,4,5]}{“id”:127,“peer_stores”:[6,4,5]}{“id”:3070,“peer_stores”:[6,4,5]} | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |

5、在剩余正常的kv节点上执行停Tikv的操作:

tiup cluster stop tidb-test -R tikv

6、在所有健康的节点上执行(操作需要确保健康的节点关闭了Tikv):

./tikv-ctl --data-dir /data1/tidb-data/tikv-20160/ unsafe-recover remove-fail-stores -s 1,5,6 --all-regions

7、停止PD节点:

tiup cluster stop tidb-test -R pd

8、重启启动PD tikv节点:

tiup cluster start tidb-test -R pd,tikv

9、检查没有处于leader状态的region

| pd-ctl -i -u http://172.16.134.133:2379» region --jq ‘.regions[]|select(has(“leader”)|not)|{id: .id,peer_stores: [.peers[].store_id]}’{“id”:4009,“peer_stores”:[6,1,5]}{“id”:6734,“peer_stores”:[6,1,5]} | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

这里发现有两个region处于没有leader的状态

10、 根据region ID,确认region属于哪张表,以备后续同步数据需要。

curl http://172.16.134.133:10080/regions/4009{“region_id”: 4009,“start_key”: “dIAAAAAAAABN”,“end_key”: “dIAAAAAAAABNX3KAAAAAAAt8fw==”,“frames”: [{“db_name”: “sbtest2”,“table_name”: “t_user”,“table_id”: 77,“is_record”: true,“record_id”: 752767}]

两个region ID均属于同一张表。

11、创建空 Region 解决 Unavailable 报错。任选一个 Store,关闭上面的 TiKV,然后执行 recreate-region

./tikv-ctl --data-dir /data1/tidb-data/tikv-20160/db recreate-region -p ‘172.16.134.133:2379’ -r 4009./tikv-ctl --data-dir /data1/tidb-data/tikv-20160/db recreate-region -p ‘172.16.134.133:2379’ -r 6734

12、停止PD节点:

tiup cluster stop tidb-test -R pd

13、重启启动PD tikv节点

tiup cluster start tidb-test -R pd,tikv

14、检查没有处于leader状态的region(要保持没有):

| pd-ctl -i -u http://172.16.134.133:2379» region --jq ‘.regions[]|select(has(“leader”)|not)|{id: .id,peer_stores: [.peers[].store_id]}’» | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

15、重新修改PD的参数

pd-ctl -i -u http://172.16.134.133:2379» config set region-schedule-limit 2048Success!» config set replica-schedule-limit 64Success!» config set leader-schedule-limit 4Success!» config set merge-schedule-limit 8Success!

16、对于所有副本丢失,无法恢复数据,从其他途径恢复数据如:

  1. 备份恢复

  2. 从上游重新导入

  3. 切换到从集群