本文介绍云数据库 MySQL 版闪回查询功能的相关信息。
在数据库运维过程中可能会发生误操作的情况,这些误操作可能会给业务带来严重的影响,因误操作导致业务受到影响时,常见的恢复手段有回档、克隆等操作,但对于少量的数据变更以及紧急故障修复而言,容易出错且耗时较长,在数据量较大时恢复时间不可控。
云数据库 MySQL 版在 InnoDB 引擎上设计和实现了闪回查询功能,仅通过简单的 SQL 语句即可查询误操作前的历史数据。
闪回查询功能的使用依赖于参数 loose_rds_flashback_query 和 loose_rds_flashback_query_window 的配置,这两个参数的信息如下:
名称 | 默认值 | 是否需要重启以生效 | 取值范围 | 级别 | 参数描述 |
---|---|---|---|---|---|
loose_rds_flashback_query | OFF | 否 | [ON|OFF] | Global | 用于开启或关闭 InnoDB 的闪回查询功能。 |
loose_rds_flashback_query_window | 900 | 否 | [1-604800] | Global | 用于设定支持闪回查询的时间段。 说明 支持闪回查询时间范围的终点只能为当前时间点,即 now()。支持闪回查询时间范围的起点由“闪回查询功能的开启时间”和“保留时间”中的较大值决定:
|
云数据库 MySQL 版提供了以下状态来显示当前闪回查询的状态:
名称 | 级别 | 含义 |
---|---|---|
Innodb_flashback_state | Global | 闪回查询运行状态
|
Innodb_flashback_state_code | Global | 闪回查询运行状态码
|
Innodb_flashback_query_views_table_open_count_by_purge_thread | Global | InnoDB purge thread 打开元数据表的次数。 |
Innodb_flashback_query_views_table_open_count_by_persist_thread | Global | Persist thread 打开元数据表的次数。 |
Innodb_flashback_query_views_table_open_count_by_user_thread | Global | User thread 打开元数据表的次数。 |
Innodb_flashback_query_start_time | Global | 闪回查询支持的时间起点。 |
Innodb_flashback_query_end_time | Global | 闪回查询支持的时间终点。 |
如果在设置参数 loose_rds_flashback_query 为 ON
之前执行闪回查询,系统会返回报错 ERROR 1030 (HY000): Got error 216 - 'Option loose_rds_flashback_query is OFF' from storage engine
。
如果查询了闪回查询时间范围之外的历史信息,系统会返回报错 ERROR 1030 (HY000): Got error 215 - 'Read view not found; Timestamp may be too old' from storage engine
。
仅支持 InnoDB 表,不支持 view 及其它引擎,不支持 last_insert_id() 等没有实际列对应的函数。
仅支持在 REPEATABLE READ、 READ COMMITTED 隔离级别下使用闪回查询。关于隔离级别更多信息,可以查看MySQL官方文档 17.7.2.1 Transaction Isolation Levels 。
不支持在 for update 类型的语句中使用闪回查询,执行会报错 ERROR 1030 (HY000): Got error 217 - 'Current transaction isolation level does not support this kind of flashback queries.' from storage engine
。
create table select 以及 insert into ... select... 仅在 READ COMMITTED 隔离级别下支持。在其他隔离级别下执行会报错 ERROR 1030 (HY000): Got error 217 - 'Current transaction isolation level does not support this kind of flashback queries.' from storage engine
。
支持的闪回查询精度为秒级,不保证时间上百分之百准确;如果一秒之内有多个改动,可能会查询到其中任何一个。
如果同一个闪回查询 statement 对一个表前后指定不同的时间戳,只能使用第一个时间戳,其余将被忽略。
在执行 DDL 操作后,之前的数据不能进行闪回查询。若闪回查询DDL之前的数据,结果可能不符合预期。
由于主节点和只读节点之间存在时间差,指定相同时间进行闪回查询,从主节点和只读节点获得的结果可能不一样。为解决这一不一致的问题,代理会将闪回查询的 SQL 只分发给主节点。
推荐在单表场景中使用闪回查询。多表闪回查询会导致查询性能劣化,不推荐在复杂查询场景下使用闪回查询。如 JOIN、子查询。
推荐使用主键来进行闪回查询,若使用二级索引来进行闪回查询则性能较差。
目前闪回查询必须指定 timestamp 字符串,不支持函数。
开启闪回查询后会延迟 Undo 日志清理,在配置的 loose_rds_flashback_query_window
时间窗口内,打开闪回查询功能会使得 Undo 表空间增长,并且数据库的查询及写入性能会有下降。不建议将 loose_rds_flashback_query_window
设置过大,建议根据业务流量情况来配置。
闪回查询的支持范围可以通过执行 show status like '%flashback%'
,在状态变量 Innodb_flashback_query_start_time
和 Innodb_flashback_query_end_time
中查看。这些时间戳的时区与 MySQL system_time_zone 一致。
关闭loose_rds_flashback_query
以后再开启,将不能查询到开启 loose_rds_flashback_query
之前的历史信息。
已创建 MySQL 8.0 实例,且实例的内核小版本为 20250215 或更新版本。关于创建实例和查看实例内核小版本的相关信息,请参见创建实例。
说明
如您的实例是在 2025 年 02 月 15 日之后创建,则自动使用 20250215 或更新的内核小版本。如您的实例是在 2025 年 02 月 15 日之前创建,则会使用 20250215 之前的内核小版本。此时,您可通过手动方式升级实例的内核小版本,详细信息,请参见手动升级实例内核小版本。
已将参数 loose_rds_flashback_query 的运行值设置为 ON
。关于修改参数的详细信息,请参见修改参数。
按需设置参数 loose_rds_flashback_query_window 的运行值。
闪回查询提供了全新的 AS OF 语法,完成前提条件后,即可使用 AS OF 语法查询指定时间的数据。 AS OF 语法如下:
SELECT column_name_list FROM table_name AS OF TIMESTAMP time_expr alias WHERE...;
语法中涉及的参数说明如下:
参数名称 | 是否必选 | 参数说明 |
---|---|---|
column_name_list | 是 | 查询的列名。 |
table_name | 是 | 表名。 |
time_expr | 是 |
|
alias | 否 | 表的别名。 |
join_cond1 | 是 | JOIN条件。 |
查询指定时间参考
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> create table t1(id int,c1 int) engine=innodb; Query OK, 0 rows affected (0.00 sec) mysql> insert into t1 values(1,1),(2,2),(3,3),(4,4); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select now(); +---------------------+ | now() | +---------------------+ | 2025-03-18 11:54:55 | +---------------------+ 1 row in set (0.00 sec) mysql> delete from t1 where id=4; Query OK, 1 row affected (0.00 sec) mysql> select * from t1; +------+------+ | id | c1 | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +------+------+ 3 rows in set (0.00 sec) mysql> select * from t1 as of timestamp '2025-03-18 11:54:55'; +------+------+ | id | c1 | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | +------+------+ 4 rows in set (0.00 sec)
通过历史数据创建表
mysql> create table t3 select * from t1 as of timestamp '2025-03-18 11:54:55'; Query OK, 4 rows affected (6.39 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from t3; +------+------+ | id | c1 | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | +------+------+ 4 rows in set (0.00 sec)
插入历史数据至表中
mysql> create table t4 like t1; Query OK, 0 rows affected (0.00 sec) mysql> insert into t4 select * from t1 as of timestamp '2025-03-18 11:54:55'; Query OK, 4 rows affected (0.09 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> select * from t4; +------+------+ | id | c1 | +------+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | +------+------+ 4 rows in set (0.00 sec)