误删数据情景分类:
如果使用 delete 语句误删了数据行,可以用 Flashback 工具通过闪回把数据恢复回来。
Flashback 恢复数据的原理,是修改 binlog 的内容,拿回原库重放。而能够使用这个方案的前提是,需要确保 binlog_format=row 和 binlog_row_image=FULL。
每一个数据行更改事件会包含两份影像(image)。
- 一份是 before image,是指被更改前的列(值);
- 一份是 after image,是指被更改后的列(值)。
默认配置下,mysql 会在 before image 和 after image 中记录更改数据行的所有列。
当删除一条数据行时,仅仅只有 before image。
当插入一条数据行时,仅仅只有after image。
当更新一条数据行时,才会同时记录 before image和 after image。
参数 binlog_row_image(默认值是 FULL) 有如下配置:
- FULL:在 before image 和 after image 中,记录所有的列值;
- MINIMAL:在 before image 和 after image 中,仅仅记录被更改的以及能够唯一识别数据行的列值;
- NOBLOB :在 before image 和 after image 中,记录所有的列值,但是 BLOB 与 TEXT 列除外(如未更改)。
具体恢复数据时,对单个事务做如下处理:
如果误操作不是一个,而是多个,比如下面三个事务:
(A)delete ...
(B)insert ...
(C)update ...
要把数据库恢复回这三个事务操作之前的状态,用 Flashback 工具解析 binlog 后,写回主库的命令是:
(reverse C)update ...
(reverse B)delete ...
(reverse A)insert ...
具体操作参考binlog2sql。
如果误删数据涉及到了多个事务的话,需要将事务的顺序调过来再执行。
不建议直接在主库上执行这些操作。恢复数据比较安全的做法,是恢复出一个备份,或者找一个从库作为临时库,在这个临时库上执行这些操作,然后再将确认过的临时库的数据,恢复回主库。
因为,一个在执行线上逻辑的主库,数据状态的变更往往是有关联的。可能由于发现数据问题的时间晚了一点儿,就导致已经在之前误操作的基础上,业务代码逻辑又继续修改了其他数据。所以,如果这时候单独恢复这几行数据,而又未经确认的话,就可能会出现对数据的二次破坏。
除了误删数据的事后处理办法,更重要是要做到事前预防。 建议如下:
使用 delete 命令删除的数据,可以用 Flashback 来恢复。而使用 truncate/drop table 和 drop database 命令删除的数据,则没办法通过 Flashback 来恢复。
因为,即使配置了 binlog_format=row,执行 truncate/drop table 和 drop database 命令时,记录的 binlog 还是 statement 格式。 binlog 里面就只有一个 truncate/drop 语句,这些信息不能恢复出数据。
这种情况下,要想恢复数据,就需要使用全量备份,加增量日志的方式了。这个方案要求线上有定期的全量备份,并且实时备份 binlog。
在这两个条件都具备的情况下,假如有人中午 12 点误删了一个库,恢复数据的流程如下:
即使这样,使用 mysqlbinlog 方法恢复数据还是不够快,主要原因有两个:
一种加速的方法是,在用备份恢复出临时实例之后,将这个临时实例设置成线上备库的从库:
图中 binlog 备份系统到线上备库有一条虚线,是指如果由于时间太久,备库上已经删除了临时实例需要的 binlog 的话,可以从 binlog 备份系统中找到需要的 binlog,再放回备库中。
假设当前临时实例需要的 binlog 是从 master.000005 开始的,但是在备库上执行 show binlogs 显示的最小的 binlog 文件是 master.000007,意味着少了两个 binlog 文件。这时,就需要去 binlog 备份系统中找到这两个文件。把之前删掉的 binlog 放回备库的操作步骤如下:
不论是把 mysqlbinlog 工具解析出的 binlog 文件应用到临时库,还是把临时库接到备库上,这两个方案的共同点是:误删库或者表后,恢复数据的思路主要就是通过备份,再加上应用 binlog 的方式。
通过利用并行复制可以加速恢复数据的过程,但是这个方案仍然存在“恢复时间不可控”的问题。
如果一个库的备份特别大,或者误操作的时间距离上一个全量备份的时间较长,比如一周一备的实例,在备份之后的第 6 天发生误操作,那就需要恢复 6 天的日志,这个恢复时间就是要按天来计算。
如果有非常核心的业务,不允许太长的恢复时间,可以考虑搭建延迟复制的备库(MySQL 5.6 版本引入)。
延迟复制的备库是一种特殊的备库,通过 CHANGE MASTER TO MASTER_DELAY = N 命令,可以指定这个备库持续保持跟主库有 N 秒的延迟。
比如把 N 设置为 3600,这就代表如果主库上有数据被误删,并且在 1 小时内发现了这个误操作命令,这个命令就还没有在这个延迟复制的备库执行。这时候到这个备库上执行 stop slave,再通过上面介绍的方法,跳过误操作命令,就可以恢复出需要的数据。
对于一个有高可用机制的 MySQL 集群来说,只要不是恶意地把整个集群删除,而只是删掉了其中某一个节点的数据的话,HA 系统就会开始工作,选出一个新的主库,从而保证整个集群的正常工作。最后,在这个节点上把数据恢复回来,再接入整个集群。
DBA 有自动化系统,SA(系统管理员)也有自动化系统,所以也许一个批量下线机器的操作,会让整个 MySQL 集群的所有节点都全军覆没。
应对这种情况,尽量把备份跨机房,或者最好是跨城市保存。