暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

TiDB的闪回

原创 薛晓刚 2025-06-16
117

经历过Oracle的闪回都觉得这是一个救命的功能

  • MySQL数据库要想实现这个功能,需要做很多人工的操作。
  • 很多数据库都可以利用日志、实时备份、延迟复制等各种组合拳解决Drop table的问题。
  • 这就使得类似闪回的功能非常人性化了

Oracle的闪回这里不介绍了

  • 能找过过去一定时间内的数据或者drop的表。

看看TiDB的闪回

  • mysql> select * from xxg limit 10;
    ±—±-------±-----+
    | id | m | n |
    ±—±-------±-----+
    | 1 | Window | 1600 |
    | 2 | Window | 1600 |
    | 3 | Window | 1600 |
    | 4 | Window | 1600 |
    | 5 | Window | 602 |
    | 6 | Window | 1600 |
    | 7 | Window | 602 |
    | 8 | Window | 1600 |
    | 9 | Window | 602 |
    | 10 | Window | 1600 |
    ±—±-------±-----+
    10 rows in set (0.00 sec)

  • 抽样获取10行数据。

mysql> select now();
±--------------------+
| now() |
±--------------------+
| 2025-06-09 16:13:38 |
±--------------------+
1 row in set (0.00 sec)

  • 获取当前时间

mysql> delete from xxg where id=1;
Query OK, 1 row affected (0.01 sec)

  • 删除ID=1的一行数据

mysql> select now();
±--------------------+
| now() |
±--------------------+
| 2025-06-09 16:14:18 |
±--------------------+
1 row in set (0.01 sec)

  • 获取当前时间

mysql> update xxg set n=300 where id=2;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

  • 更新ID=2的一行数据

mysql> select now();
±--------------------+
| now() |
±--------------------+
| 2025-06-09 16:14:43 |
±--------------------+
1 row in set (0.00 sec)

mysql> select * from xxg limit 10;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 300 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
±—±-------±-----+
10 rows in set (0.01 sec)

  • 现在数据已经改变了。接下来要看闪回能做什么和不能做什么了。

mysql> select * from xxg as of timestamp ‘2025-06-09 16:12:50’ limit 10;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 1 | Window | 1600 |
| 2 | Window | 1600 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
±—±-------±-----+
10 rows in set (0.01 sec)

  • 可以查询到最开始的数据状态。

mysql> select * from xxg as of timestamp ‘2025-06-09 16:14:30’ limit 10;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 1600 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
±—±-------±-----+
10 rows in set (0.01 sec)

  • 也可以查询到只删除一条数据的时间点的状态。

mysql> select * from xxg as of timestamp ‘2025-06-09 16:14:50’ limit 10;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 300 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
±—±-------±-----+
10 rows in set (0.00 sec)

mysql> select * from xxg limit 10;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 300 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
±—±-------±-----+
10 rows in set (0.01 sec)

  • 当然最后时间也就是现在。

开始数据恢复

mysql> create table t1 as select * from xxg as of timestamp ‘2025-06-09 16:12:50’ limit 10;
ERROR 1105 (HY000): ‘CREATE TABLE … SELECT’ is not implemented yet

  • 不支持我们传统的SQL

mysql> create table t1 as select * from xxg where 1=2;
ERROR 1105 (HY000): ‘CREATE TABLE … SELECT’ is not implemented yet

  • 即使是做建立一个表结构这样也不行。

mysql> create table t1 as select * from xxg;
ERROR 1105 (HY000): ‘CREATE TABLE … SELECT’ is not implemented yet
mysql>
mysql> show create table xxg\G
*************************** 1. row ***************************
Table: xxg
Create Table: CREATE TABLE xxg (
id int NOT NULL AUTO_INCREMENT,
m varchar(10) DEFAULT NULL,
n int DEFAULT NULL,
PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=12329325
1 row in set (0.00 sec)

  • 只能根据原表的表结构,老老实实的建议个空表。

mysql> create table t1 (
-> id int NOT NULL AUTO_INCREMENT,
-> m varchar(10) DEFAULT NULL,
-> n int DEFAULT NULL,
-> PRIMARY KEY (id));
Query OK, 0 rows affected (0.52 sec)

mysql> insert into t1 select * from xxg as of timestamp ‘2025-06-09 16:12:50’ limit 10;
ERROR 8135 (HY000): can not set different time in the as of

  • 采用insert into的方法,不支持。

  • 需要用的命令是import into,这里注意后面还要带一个from。特殊的语法。

mysql> import into t1 from select * from xxg as of timestamp ‘2025-06-09 16:12:50’ limit 10;
ERROR 9006 (HY000): GC life time is shorter than transaction duration, transaction starts at 2025-06-09 16:12:50 +0800 CST, GC safe point is 2025-06-09 16:24:55.441 +0800 CST

  • 终于解决了语法问题。带来了一个姓问题。显示生命时间太短。想起了我之前Oracle闪回建表的时候,ogg下游undo不足的场景。
  • 查到了数据保留时间,默认10分钟。我个人觉得的太短了。正式环境怎么也应该一个小时。

mysql> show variables like ‘tidb_gc_life_time’;
±------------------±------+
| Variable_name | Value |
±------------------±------+
| tidb_gc_life_time | 10m0s |
±------------------±------+
1 row in set (0.00 sec)

  • 目前先按照10分钟的进行实践。

mysql> select now();
±--------------------+
| now() |
±--------------------+
| 2025-06-09 16:50:44 |
±--------------------+
1 row in set (0.00 sec)

mysql> delete from xxg where id<21;
Query OK, 10 rows affected (0.01 sec)

mysql> select now();
±--------------------+
| now() |
±--------------------+
| 2025-06-09 16:50:58 |
±--------------------+
1 row in set (0.00 sec)

mysql> select * from xxg where id<21;
Empty set (0.01 sec)

mysql> select * from xxg where id<22;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 21 | Window | 1600 |
±—±-------±-----+
1 row in set (0.00 sec)

mysql> import into t1 from select * from xxg as of timestamp ‘2025-06-09 16:45:50’ where id<21;
Query OK, 19 rows affected (0.92 sec)
Records: 19, ID: e8fe2d19-0061-41a3-a4d0-6eab82fd6bbb

mysql> select * from t1;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 300 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
| 12 | Window | 1600 |
| 13 | Window | 602 |
| 14 | Window | 1600 |
| 15 | Window | 1600 |
| 16 | Window | 1600 |
| 17 | Window | 602 |
| 18 | Window | 1600 |
| 19 | Window | 602 |
| 20 | Window | 1600 |
±—±-------±-----+
19 rows in set (0.01 sec)

  • 闪回建表成功。

  • 下面尝试一下drop和truncate。Oracle的闪回是不支持truncate的。
    mysql> select now();
    ±--------------------+
    | now() |
    ±--------------------+
    | 2025-06-09 16:53:06 |
    ±--------------------+
    1 row in set (0.00 sec)

mysql> drop table t1;
Query OK, 0 rows affected (0.52 sec)

mysql> truncate table t2;
Query OK, 0 rows affected (0.52 sec)

mysql> select now();
±--------------------+
| now() |
±--------------------+
| 2025-06-09 16:53:56 |
±--------------------+
1 row in set (0.00 sec)

mysql> flashback table t1;
Query OK, 0 rows affected (0.56 sec)

mysql> flashback table t2;
ERROR 1050 (42S01): Table ‘t2’ already exists
mysql> flashback table t2 to t3;
Query OK, 0 rows affected (0.54 sec)

  • truncate的闪回要注意,表结构还在。不能直接flashback。需要重命名定向一下。

mysql> select * from t1;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 300 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
| 12 | Window | 1600 |
| 13 | Window | 602 |
| 14 | Window | 1600 |
| 15 | Window | 1600 |
| 16 | Window | 1600 |
| 17 | Window | 602 |
| 18 | Window | 1600 |
| 19 | Window | 602 |
| 20 | Window | 1600 |
±—±-------±-----+
19 rows in set (0.00 sec)

mysql> select * from t2;
Empty set (0.00 sec)

mysql> select * from t3;
±—±-------±-----+
| id | m | n |
±—±-------±-----+
| 2 | Window | 300 |
| 3 | Window | 1600 |
| 4 | Window | 1600 |
| 5 | Window | 602 |
| 6 | Window | 1600 |
| 7 | Window | 602 |
| 8 | Window | 1600 |
| 9 | Window | 602 |
| 10 | Window | 1600 |
| 11 | Window | 1600 |
| 12 | Window | 1600 |
| 13 | Window | 602 |
| 14 | Window | 1600 |
| 15 | Window | 1600 |
| 16 | Window | 1600 |
| 17 | Window | 602 |
| 18 | Window | 1600 |
| 19 | Window | 602 |
| 20 | Window | 1600 |
±—±-------±-----+
19 rows in set (0.01 sec)

  • 时间完毕

注意点

  • 闪回的生命周期
  • import的语法
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论