作者:网易数据库团队
在《删库不跑路!数据库误删有几种恢复方式?》一文中,我们曾提到处理数据库误删除的几种方法,其中一种是数据闪回(flashback),目前我们在开源实现的基础上,对DDL flashback方案进行了优化,解决了其存在的兼容性等问题。现对其进行简单介绍。
背景需求
Flashback概念最早出自Oracle,用于快速恢复用户的误操作,Flashback for MySQL是由阿里彭立勋于2012-5-2贡献的patch给MySQL官方5.5.18版本,此版本基于mysqlbinlog工具通过-B,--flashback参数对binlog中的DML逆操作生成闪回日志进行恢复,但此版本不支持DDL flashback。
随后2012-7-10阿里林晓斌贡献的patch支持DDL flashback,原理是通过rename的方式备份数据表,并增加FLASHBACK_EVENT event类型记录rename后的statement信息,通过mysqlbinlog工具-B,--flashback参数生成闪回log进行恢复。
DDL flashback版本存在三个问题:兼容性问题:引入了新的event类型FLASHBACK_EVENT,如果是主从复制场景,从库MySQL的版本未引入DDL flashback 功能时,从库回放relay-log时会因解析不了FLASHBACK_EVENT event而造成复制中断。
可维护性:回收库#bak_database的创建是写在系统库脚本中,随着mysqld的初始化进行创建,未记录binlog,同样如果从库版本没有引入DDL flashback功能,那么复制过来的rename语句会因为不存在#bak_database库而执行失败,造成主从不一致。
垃圾清理:#bak_database备份库没有进行垃圾回收,会造成磁盘空间冗余占用。
为此我们在InnoSQL 5.7和8.0版本上对DDL Flashback进行了优化改进,解决了上述问题,目前暂时支持drop/truncate两个常用的删除数据的操作,下面将简单介绍实现原理。
实现原理
总体思想是回收站原理,通过rename的方式将drop/truncate的表数据迁移至备库#bak_database中,通过mysqlbinlog工具-B,--flashback参数生成回滚语句进行数据恢复,并在mysqld启动时新增drop_flashback_table自动清理线程,定期自动清理#bak_database中的过期的备份数据,防止磁盘空间冗余占用。下面从这三部分功能的实现进行说明。
数据备份
在MySQL server层修改mysql_execute_command()函数中执行drop/truncate部分的代码,首先判断全局变量is_bak_database_created,如果为true,则说明#bak_database已经被创建,其他DDL操作直接使用备库,否则在DDL执行前先通过“CREATE DATABASE IF NOT EXISTS `#bak_database`”创建备库,这种方式可以通过主从复制同步到从库,这样避免了前述的问题2。
继续判断如果开启sql_flashback,则把执行语句修改成rename操作将数据备份到#bak_database库中,同时将对应的回滚语句通过注释的方式一起记录binlog,如下图1-3中黄色部分,分别列出了drop/truncate语句对应的binlog记录中rename操作和回滚操作内容。图1 truncate table语句binlog记录图2 drop table语句binlog记录图3 drop database语句binlog记录
数据恢复
通过mysqlbinlog工具-B,--flashback参数生成回滚语句进行数据恢复,-B参数是继承DML闪回实现版本,在打印binlog内容的函数Query_log_event::print中增加判断,如果mysqlbinlog开启-B参数并且event类型是QUERY_EVENT时,则解析/* FLASHBACK:xxxx */部分的内容,输出回滚语句进行数据恢复。
这样没有新增event类型,回滚操作通过记录在rename操作binlog注释/**/中,并通过mysqlbinlog解析来完成。避免了前述的问题1。
清理数据
在启动mysqld的过程中,新增drop_flashback_table线程,通过循环查询information_schema.tables中信息判断#bak_database库中是否存在备份表,如果存在,并且备份表的时间戳大于当前系统变量drop_flashback_timeout设置的过期清理时间,则对备份表执行drop操作进行清理,防止磁盘空间冗余占用。避免了前述的问题3。
参数说明
sql_flashback
开启/关闭DDL(drop/truncate)闪回功能,如:set sql_flashback = on/off,默认关闭,提供global和session 2个维度。并通过mysqlbinlog -B生成DDL回滚语句进行闪回。
例1:
create table test(a int);
insert into test values(1);
flush logs;
set sql_flashback=on;
drop table test;
mysqlbinlog -B mysql-bin.000003 > fb.sql
drop_flashback_timeout
设置备份库#bak_database中数据的过期时间,如:
set global drop_flashback_timeout=60;
默认过期时间是48小时,过期后备份数据将会被自动清理,可设置过期时间范围1min~365days。
例2:过期清理前#bak_database中的备份表。
客户端设置过期时间
set global drop_flashback_timeout=60;
60s后看过期表已经被清理。
mysql-err.log记录清理提示信息。