背景
在 DBA 的日常工作中,误删除数据是一种常见但令人头疼的问题。作为数据库管理员,DBA 承担着维护数据库完整性和安全性的责任。误删测试数据可能造成项目延期,影响测试,重复工作,误删生产数据可能导致业务中断,客户数据丢失,导致公司面临法律风险。一旦发生误删除,DBA 需要迅速应对,本文主要介绍在 TiDB 数据库中,误删数据后的常见应急措施。
常见场景
- drop database 误删除数据库
- truncate table 误清空表
- drop table 误删除表
- 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到另一张表,那样误删除/更新数据场景下的恢复速度就更快了。