1. 使用delete语句误删数据行;
    2. 使用drop table或者truncate table语句误删数据表;
    3. 使用drop database语句误删数据库;
    4. 使用rm命令误删整个MySQL实例

1 使用delete语句误删数据行

Flashback工具通过闪回把数据恢复回来。 Flashback恢复数据的原理, 是修改binlog的内容, 拿回原库重放。 而能够使用这个方案的前提是, 需要确保binlog_format=row和 binlog_row_image=FULL

具体恢复数据时, 对单个事务做如下处理:
1. 对于insert语句, 对应的binlog event类型是Write_rows event, 把它改成Delete_rows event
即可;
2. 同理, 对于delete语句, 也是将Delete_rows event改为Write_rows event
3. 而如果是Update_rows的话, binlog里面记录了数据行修改前和修改后的值, 对调这两行的位置即可

如果误操作不是一个, 而是多个,下面三个事务?

  1. (A)delete ...
  2. (B)insert ...
  3. (C)update ...

用Flashback工具解析binlog后, 写回主库的命令是:

(reverse C)update ...
(reverse B)delete ...
(reverse A)insert ...

误删数据涉及到了多个事务的话, 需要将事务的顺序调过来再执行

不建议直接在主库上执行这些操作:
找一个从库作为临时库, 在这个临时库上执行这些操作, 然后再将确认过的临时库的数据, 恢复回主库。

一个在执行线上逻辑的主库, 数据状态的变更往往是有关联的。 业务代码逻辑又继续修改了其他数据。 所以, 如果这时候单独恢复这几行数据, 而又未经确认的话, 就可能会出现对数据的二次破。

  1. 把sql_safe_updates参数设置为on。 这样一来, 如果我们忘记在delete或者update语句中写where条件, 或者where条件里面没有包含索引字段的话, 这条语句的执行就会报错。
    2. 代码上线前, 必须经过SQL审计。


    2 误删库/表


    两个前提:1 定期备份数据库和
    2定期备份binlog

恢复数据的流程如下:
1. 取最近一次全量备份, 假设这个库是一天一备, 上次备份是当天0点;
2. 用备份恢复出一个临时库;
3. 从日志备份里面, 取出凌晨0点之后的日志;
4. 把这些日志, 除了误删除数据的语句外, 全部应用到临时库

image.png

可能会很慢:
1. 为了加速数据恢复, 如果这个临时库上有多个数据库, 你可以在使用mysqlbinlog命令时, 加上一个–database参数, 用来指定误删表所在的库。 这样, 就避免了在恢复数据时还要应用
其他库日志的情况。
2. 在应用日志的时候, 需要跳过12点误操作的那个语句的binlog:如果原实例没有使用GTID模式, 只能在应用到包含12点的binlog文件的时候, 先用–stop-position参数执行到误操作之前的日志, 然后再用–start-position从误操作之后的日志继续执行;
如果实例使用了GTID模式, 就方便多了。 假设误操作命令的GTID是gtid1, 那么只需要执行set gtid_next=gtid1;begin;commit; 先把这个GTID加到临时实例的GTID集合, 之后按顺序执行binlog的时候, 就会自动跳过误操作的语句。

不过, 即使这样, 使用mysqlbinlog方法恢复数据还是不够快, 主要原因有两个:
1. 如果是误删表, 最好就是只恢复出这张表, 也就是只重放这张表的操作, 但是mysqlbinlog工具并不能指定只解析一个表的日志;
2. 用mysqlbinlog解析出日志应用, 应用日志的过程就只能是单线程。 我们在第26篇文章中介绍的那些并行复制的方法, 在这里都用不上。

一种加速的方法是, 在用备份恢复出临时实例之后, 将这个临时实例设置成线上备库的从库,这样:
1. 在start slave之前, 先通过执行change replication filter replicate_do_table = (tbl_name) 命令, 就可以让临时库只同步误操作的表;
2. 这样做也可以用上并行复制技术, 来加速整个数据恢复过程

image.png

图中binlog备份系统到线上备库有一条虚线, 是指如果由于时间太久, 备库上已经删除了临时实例需要的binlog的话, 我们可以从binlog备份系统中找到需要的binlog, 再放回备库
中。
假设, 我们发现当前临时实例需要的binlog是从master.000005开始的, 但是在备库上执行show binlogs 显示的最小的binlog文件是master.000007, 意味着少了两个binlog文件。 这时, 我们就
需要去binlog备份系统中找到这两个文件。把之前删掉的binlog放回备库的操作步骤, 是这样的:
1. 从备份系统下载master.000005和master.000006这两个文件, 放到备库的日志目录下;
2. 打开日志目录下的master.index文件, 在文件开头加入两行, 内容分别是
“./master.000005”和“./master.000006”;
3. 重启备库, 目的是要让备库重新识别这两个日志文件;
4. 现在这个备库上就有了临时库需要的所有binlog了, 建立主备关系, 就可以正常同步了

一个系统不可能备份无限的日志, 你还需要根据成本和磁盘空间资源, 设定一个日志保留的天数。 如果你的DBA团队告诉你, 可以保证把某个实例恢复到半个月内的任意时间点, 这就表示备份系统保留的日志时间就至少是半个月。 数据恢复功能做成自动化工具


3 延迟复制备库

一个库的备份特别大, 或者误操作的时间距离上一个全量备份的时间较长, 比如一周一备的实例, 在备份之后的第6天发生误操作, 那就需要恢复6天的日志, 这个恢复时间可能是要按天来计算的
考虑搭建延迟复制的备库。 这个功能是MySQL 5.6版本引入的:
延迟复制的备库是一种特殊的备库, 通过 CHANGE MASTER TO MASTER_DELAY = N命令,可以指定这个备库持续保持跟主库有N秒的延迟

比如你把N设置为3600, 这就代表了如果主库上有数据被误删了, 并且在1小时内发现了这个误操作命令, 这个命令就还没有在这个延迟复制的备库执行。 这时候到这个备库上执行stop slave, 再通过之前介绍的方法, 跳过误操作命令, 就可以恢复出需要的数据。这样的话, 你就随时可以得到一个, 只需要最多再追1小时, 就可以恢复出数据的临时实例

4 预防误删库/表的方法

1 账号分离。 这样做的目的是, 避免写错命令
2 制定操作规范。 这样做的目的, 是避免写错要删除的表名


5 rm删除数据

一个有高可用机制的MySQL集群来说, 最不怕的就是rm删除数据了。 只要不是恶意地把整个集群删除, 而只是删掉了其中某一个节点的数据的话, HA系统就会开始工作, 选出一个新的主库