1
3
2
0
专栏/.../

TiDB误删数据恢复

 longzhuquan  发表于  2024-03-07

背景

在 DBA 的日常工作中,误删除数据是一种常见但令人头疼的问题。作为数据库管理员,DBA 承担着维护数据库完整性和安全性的责任。误删测试数据可能造成项目延期,影响测试,重复工作,误删生产数据可能导致业务中断,客户数据丢失,导致公司面临法律风险。一旦发生误删除,DBA 需要迅速应对,本文主要介绍在 TiDB 数据库中,误删数据后的常见应急措施。

常见场景

  1. drop database 误删除数据库
  2. truncate table 误清空表
  3. drop table 误删除表
  4. delete、update 误删除/更新数据

恢复方法

应急操作

TiDB 的事务的实现采用了 MVCC(多版本并发控制)机制,当新写入的数据覆盖旧的数据时,旧的数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。TiDB 通过定期 GC 的机制来清理不再需要的旧数据。因此,GC 没有过期是我们进行数据恢复的基础。

在生产环境中,可以通过适当设置垃圾收集(GC)参数来应对误删除场景,但同时也需要考虑历史版本过多可能引起的性能和容量上的开销。

当出现误删除数据情况时,应在第一时间调整 GC 相关参数,增加 GC 保留时长,操作方法如下:

# 查看gc参数配置
show variables like '%tidb_gc_life_time%';
# 调整gc参数配置
set global tidb_gc_life_time='48h';

drop database 误删除数据库恢复(TiDB v6.4之前)

确定误操作时间,可以通过 admin show ddl jobs 语句确认误操作时间

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  |
+--------+---------+------------+---------------+--------------+-----------+----------+-----------+---------------------+---------------------+--------+
|    116 | test1   |            | drop schema   | queueing     |       102 |        0 |         0 | 2024-03-07 12:59:56 | 2024-03-07 12:59:56 | synced |
|    115 | test1   | t2         | add index     | public       |       102 |      112 |     10000 | 2024-03-07 12:41:29 | 2024-03-07 12:41:33 | synced |
|    114 | test1   | t1         | add index     | public       |       102 |      110 |     10000 | 2024-03-07 12:41:23 | 2024-03-07 12:41:27 | synced |
|    113 | test1   | t2         | create table  | public       |       102 |      112 |         0 | 2024-03-07 12:41:21 | 2024-03-07 12:41:21 | synced |
|    111 | test1   | t1         | create table  | public       |       102 |      110 |         0 | 2024-03-07 12:41:21 | 2024-03-07 12:41:21 | synced |
|    109 | test    | t2         | add index     | public       |         1 |      106 |     10000 | 2024-03-07 12:40:47 | 2024-03-07 12:40:51 | synced |
|    108 | test    | t1         | add index     | public       |         1 |      104 |     10000 | 2024-03-07 12:40:41 | 2024-03-07 12:40:45 | synced |
|    107 | test    | t2         | create table  | public       |         1 |      106 |         0 | 2024-03-07 12:40:39 | 2024-03-07 12:40:40 | synced |
|    105 | test    | t1         | create table  | public       |         1 |      104 |         0 | 2024-03-07 12:40:38 | 2024-03-07 12:40:38 | synced |
|    103 | test1   |            | create schema | public       |       102 |        0 |         0 | 2024-03-07 12:35:14 | 2024-03-07 12:35:14 | synced |
+--------+---------+------------+---------------+--------------+-----------+----------+-----------+---------------------+---------------------+--------+
10 rows in set (0.73 sec)

或通过 TiDB Dashboard 日志搜索功能确认误操作时间

确认下删除时间没超过 GC 时间,即下面SQL查询出来的 tikv_gc_safe_point 小于数据删除时间

MySQL [test1]> SELECT * FROM mysql.tidb WHERE variable_name = 'tikv_gc_safe_point';
+--------------------+-----------------------------+--------------------------------------------------------------+
| VARIABLE_NAME      | VARIABLE_VALUE              | COMMENT                                                      |
+--------------------+-----------------------------+--------------------------------------------------------------+
| tikv_gc_safe_point | 20240307-13:28:51.993 +0800 | All versions after safe point can be accessed. (DO NOT EDIT) |
+--------------------+-----------------------------+--------------------------------------------------------------+
1 row in set (0.01 sec)

使用 SQL 确认下删除时间。

MySQL [test]> set session tidb_snapshot='2024-03-07 12:59:56';
Query OK, 0 rows affected (0.00 sec)

MySQL [test]> show databases;
+--------------------+
| Database           |
+--------------------+
| INFORMATION_SCHEMA |
| METRICS_SCHEMA     |
| PERFORMANCE_SCHEMA |
| mysql              |
| test               |
| test1              |
| tpcc               |
+--------------------+
7 rows in set (0.00 sec)

MySQL [test]> set session tidb_snapshot='2024-03-07 12:59:57';
Query OK, 0 rows affected (0.00 sec)

MySQL [test]> show databases;
+--------------------+
| Database           |
+--------------------+
| INFORMATION_SCHEMA |
| METRICS_SCHEMA     |
| PERFORMANCE_SCHEMA |
| mysql              |
| test               |
| tpcc               |
+--------------------+
6 rows in set (0.01 sec)

使用 dumpling 导出要保留的数据

tiup dumpling -uroot  -h192.168.0.1 -P4000 -B test1 --filetype sql -t 4 -F 256MiB -o ./dump_test1 -L ./dump_test1.log --snapshot "2024-03-07 12:59:56"

使用 lighting 导出要保留的数据

tiup tidb-lightning -backend tidb -d ./dump_test1 -tidb-host 192.168.0.1 -tidb-password "" -tidb-port 4000 -tidb-user root

数据恢复完成

DROP DATABASE 误删除数据库恢复(TiDB v6.4之后)

从 TiDB v6.4 版本开始,TiDB 引入了 FLASHBACKUP DATABASE 语法,该语法允许快速恢复误删除的数据库,无需再确认误操作时间,极大地加快了误删除数据库恢复的速度。值得注意的是,该功能同样依赖于 GC(垃圾收集)机制。

MySQL [(none)]> flashback database test1;
Query OK, 0 rows affected (0.31 sec)

MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| INFORMATION_SCHEMA |
| METRICS_SCHEMA     |
| PERFORMANCE_SCHEMA |
| mysql              |
| result             |
| test               |
| test1              |
| tpcc               |
+--------------------+
8 rows in set (0.00 sec)

MySQL [(none)]> use test1;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [test1]> show tables;
+-----------------+
| Tables_in_test1 |
+-----------------+
| t1              |
| t2              |
+-----------------+
2 rows in set (0.00 sec)

TRUNCATE TABLE 误清空表

使用 FLASHBACK TABLE 语句可以快速恢复误清空表导致的数据丢失。值得注意的是,该语句仅能恢复上一次 TRUNCATE 操作丢失的数据。如果需要恢复多次 TRUNCATE 操作前的数据,可以按照 TiDB v6.4 之前版本的drop database误删除库的恢复操作进行处理。值得注意的是,该功能同样依赖于 GC(垃圾收集)机制。

MySQL [test1]> truncate table t1;
Query OK, 0 rows affected (0.52 sec)

MySQL [test1]> select count(1) from t1;
+----------+
| count(1) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

MySQL [test1]> insert into t1 select * from t2 limit 3;
Query OK, 3 rows affected (0.02 sec)
Records: 3  Duplicates: 0  Warnings: 0

MySQL [test1]> select * from t1;
+------+------------+------------+------------+
| id   | col1       | col2       | col3       |
+------+------------+------------+------------+
|    1 | 0f74fa3c18 | 66b149a20e | 0e1e7bf8c8 |
|    2 | f0f976f0d3 | 75fc7f6174 | d562074867 |
|    3 | 579cfa1074 | 1d74595138 | e9220d8e86 |
+------+------------+------------+------------+
3 rows in set (0.00 sec)

MySQL [test1]> flashback table t1;
ERROR 1050 (42S01): Table 't1' already exists
MySQL [test1]> flashback table t1 to t1_old;
Query OK, 0 rows affected (2.03 sec)

MySQL [test1]> select count(1) from t1_old;
+----------+
| count(1) |
+----------+
|    10000 |
+----------+
1 row in set (0.01 sec)

DROP TABLE 误删除表

DROP TABLE 误删除表与 TRUNCATE TABLE 误清空数据的处理方式类似,这里就不再赘述。

DELETE、UPDATE 误删除/更新数据

DELETE、UPDATE 误删除/更新数据与 TiDB v6.4 版本前的 DROP DATABASE 误删除库处理方式相似,只不过在确认误操作时间上相对困难。以下是确定误操作时间的可能思路:

  • TiDB 慢查询日志:如果 DELETE 或 UPDATE 的 SQL 执行时长超过慢查询日志阈值,相关信息将被记录在慢查询日志中。
  • 审计日志:包括 TiDB 审计日志、堡垒机审计、第三方数据库审计软件等。通过审计日志可以追踪到数据库操作的详细信息,包括误删除或误更新的时间戳。
  • 误操作人员的时间描述:误操作人员提供的关于误操作发生时间的描述。虽然这种方式相对主观,但可能有助于缩小确认误操作时间的范围。

通过以上信息,结合 TiDB 提供的 tidb_snapshot 功能,可以进一步确定误操作的确切时间,为后续数据的准确恢复提供基础。

总结建议

总结:TiDB 数据库针对数据库误删除的场景处理方式还是很友好的,多数场景下都能比较快速的找回数据。但恢复比较依赖 GC 机制,如果超过了 GC 的时长,就是能使用备份对误删除的数据进行恢复了。

建议:如果tidb_snapshot功能下能够支持将查询出来的数据insert into/import into到另一张表,那样误删除/更新数据场景下的恢复速度就更快了。

1
3
2
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论