利用MySQL闪回技术恢复误删除误更改的数据

笔者相信很多人都遇到过忘带where条件或者where条件漏写了一个和写错了的情况,结果执行了delete/update后把整张表的数据都给改了。传统的解决方法是:利用最近的全量备份+增量binlog备份,恢复到误操作之前的状态,但是此方法有一个弊端,那就是随着表的记录增大,binlog的增多,恢复起来会很费时费力。

现在有一个简单的方法,可以恢复到误操作之前的状态。听起来这方法似乎利用的是Oracle的闪回功能,但MySQL目前(包括最新的V5.7版本与MariaDB10.1分支版本)还不具有这样的功能,不过,我们完全可以模拟出这一功能来。使用该方法前,记得要将binlog日志设置为binlog_format = ROW,如果是STATEMENT,这个方法是无效的。切记!!

在讲解闪回恢复前,我们首先要了解一下闪回的概念。闪回就类似Windows里的回收站功能。MySQL实现闪回,是通过模拟的方式(解析binlog)来恢复数据。2012年,原淘宝彭立勋针对MySQL5.5版本,对rows格式的binlog可以进行逆向操作。delete反向生成insert,update生成反向的update。2016年,原支付宝楼方鑫针对MySQL5.6版本也同样实现了此功能。

但目前还没有针对MariaDB 10的版本,由于binlog格式与MySQL不同,无法使用此工具恢复数据,所以笔者也是通过解析binlog,用sed命令进行逆向操作,针对delete反向生成replace into,update反向生成replace into,适用于MySQL/MariaDB/Percona版本。

笔者借鉴了Percona官方博客的思路实现。https://www.percona.com/blog/2012/10/19/recovering-from-a-bad-update-statement/

注!DROP/TRUNCATE操作无法使用闪回,需要全量备份+binlog增量备份恢复。

下面笔者通过一个示例给大家演示一下。

u 误删除闪回步骤

现有一张test表,总共有10条记录,查询结果如图(1)所示。

图(1) test表数据

下面在执行delete删除操作的时候,忘加where条件,导致全表记录被删除,如图(2)所示。

图(2) test表的记录全表被删除

恢复过程

1、找到误删除的binlog保存下来

# mysqlbinlog --base64-output=decode-rows -v -v --start-datetime="2016-05-04

13:15:00" mysql-bin.000001 | grep -B 50 '### DELETE FROM `test`.`test`' | more

通过时间定位到全表删除的语句,如图(3)所示。

图(3) delete删除

此时,请注意看笔者用红色框标出的@1,@2,@3,@4,这里对应原表字段分别是id,k,c,pad,后面的=等号则对应的原表被删除的记录。

下一步,只需把这一段binlog保存下来,以待后面的恢复,执行命令如下。

# mysqlbinlog --base64-output=decode-rows -v -v mysql-bin.000001 | sed -n

'/end_log_pos 1049/,/COMMIT/p' | tail -n +2 > /root/recover.binlog

查看recover.binlog,我们得到了如下内容:

# cat /root/recover.binlog

### DELETE FROM `test`.`test`

### WHERE

###   @1=1 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=2 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=3 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=4 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=5 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=6 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=7 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=8 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=9 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

### DELETE FROM `test`.`test`

### WHERE

###   @1=10 /* INT meta=0 nullable=0 is_null=0 */

###   @2=0 /* INT meta=0 nullable=0 is_null=0 */

###   @3='' /* STRING(360) meta=61032 nullable=0 is_null=0 */

###   @4='qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' /*

STRING(180) meta=65204 nullable=0 is_null=0 */

# at 1049

#160504 13:16:08 server id 17128  end_log_pos 1076      Xid = 3225

COMMIT/*!*/;

2、逆向将delete转为replace into可执行SQL语句

# cat recover.binlog | sed -n '/###/p' | sed 's/### //g;s/\/\*.*/,/g;s/DELETE

FROM/\nREPLACE INTO/g;s/WHERE/SELECT/g' | sed -r 's/(@4.*),/\1;/g' | sed

's/@[1-9].*=//g' > /root/recover.sql

注:@4代表字段的最后一位,如果你的表有10个字段,要将其改为@10

查看recover.sql,我们得到了如下内容:

# cat /root/recover.sql

REPLACE INTO `test`.`test`

SELECT

1 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

2 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

3 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

4 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

5 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

6 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

7 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

8 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

9 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

REPLACE INTO `test`.`test`

SELECT

10 ,

0 ,

'' ,

'qqqqqqqqqqwwwwwwwwwweeeeeeeeeerrrrrrrrrrtttttttttt' ;

3、恢复

最激动人心的一幕到来了,现在就要进行最后一步的恢复操作了,把这些binlog转换成SQL语句,然后将其导入进去,执行命令如下:

# source /root/recover.sql

大功告成!你也快动手试试吧!友情提醒:千万不要在生产环境下测试哦。

u 误更改update闪回步骤

1、同理第一步

2、逆向将delete转为replace into可执行SQL语句

# cat recover.binlog | sed -n '/###/p' | sed 's/### //g;s/\/\*.*/,/g;' | sed -n '/UPDATE

`test`.`test`/,/SET/p'| sed 's/UPDATE/REPLACE

INTO/g;s/WHERE/SELECT/g;s/SET/\n/g' |sed -r 's/(@4.*),/\1;/g' | sed

's/@[1-9].*=//g' > /root/recover.sql

3、同理第三步

mysql 闪回原理的更多相关文章

  1. MySQL闪回原理与实战

    本文将介绍闪回原理,给出笔者的实战经验,并对现存的闪回工具作比较. DBA或开发人员,有时会误删或者误更新数据,如果是线上环境并且影响较大,就需要能快速回滚.传统恢复方法是利用备份重搭实例,再应用去除 ...

  2. (4.11)mysql备份还原——mysql闪回技术(基于binlog)

    0.闪回技术与工具简介 mysql闪回工具比较流行三大类: [0.1]官方的mysqlbinlog:支持数据库在线/离线,用脚本处理binlog的输出,转化成对应SQL再执行.通用性不好,对正则.se ...

  3. Mysql闪回工具之binlog2sql的原理及其使用

    生产上误删数据.误改数据的现象也是时常发生的现象,作为运维这时候就需要出来补锅了,最开始的做法是恢复备份,然后从中找到需要的数据再进行修复,但是这个时间太长了,对于大表少数数据的修复来讲,动作太大,成 ...

  4. MySQL 闪回工具之 binlog2sql

    生产上误删数据.误改数据的现象也是时常发生的现象,作为 DBA 这时候就需要出来补锅了,最开始的做法是恢复备份,然后从中找到需要的数据再进行修复,但是这个时间太长了,对于大表少数数据的修复来讲,动作太 ...

  5. 【oracle11g,13】表空间管理2:undo表空间管理(调优) ,闪回原理

    一.undo空间原理: dml操作会产生undo数据. update时,sever process 会在databuffer 中找到该记录的buffer块,没有就从datafile中找并读入data ...

  6. MySQL闪回工具之myflash 和 binlog2sql

    MySQL闪回工具之:binlog2sql  https://github.com/danfengcao/binlog2sql MYSQL Binglog分析利器:binlog2sql使用详解  :h ...

  7. MySQL闪回工具之binlog2sql

    一.binlog2sql 1.1 安装binlog2sql git clone https://github.com/danfengcao/binlog2sql.git && cd b ...

  8. 技术分享 | 浅谈MySQL闪回的实现

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 1.闪回实现原理 2.binlog文件格式初探 3.闪回实现过程 1.闪回实现原 ...

  9. mysql 闪回测试

    由于前面出现过几个需求,或者误操作,或者测试,需要我把某张表恢复到操作之前的一个状态,前面在生产中有过几次经历,实在太痛苦了,下面是一张表被误删除了,我的步骤是: 1  用全备恢复整个库(恢复到其他环 ...

随机推荐

  1. Android 1.5-7.0(持续更新)安全机制一览

    Android 1.5 ProPolice to prevent stack buffer overruns (-fstack-protector),在缓冲区buffer与返回地址之间加入Canary ...

  2. 关于PHP程序员技术职业生涯规划[转]

    看到很多PHP程序员职业规划的文章,都是直接上来就提Linux.PHP.MySQL.Nginx.Redis.Memcache.jQuery这些,然后就直接上手搭环境.做项目,中级就是学习各种PHP框架 ...

  3. 在城市后面加上省,市,区 以及将MySQL入库脚本封装成class

    在城市后面加省,市,区时,使用过滤器和for循环,if判断 一起使用.   自定义一个过滤器 def my_detail(val):                                  ...

  4. ROI-Align解决方案

    https://yq.aliyun.com/articles/558181 Mask R-CNN与Faster R-CNN相似,Faster R-CNN是two-stage的,其中第一个stage是R ...

  5. fold算法(拉格朗日插值)

    如果打表发现某个数列: 差分有限次之后全为0 例如: 2017新疆乌鲁木齐ICPC现场赛D题 ,,,,,,,,,,…… [2018江苏南京ICPC现场赛也有这样的题目] 那么可以使用以下黑科技计算出第 ...

  6. sudo安装软件的时候出现Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)

    最近在安装软件的时候经常出现下面提示: E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavai ...

  7. 欢迎大家使用Druid,常见问题在这里解答,希望对大家有所帮助

    https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98 欢迎大家使用Druid,常见问题在这里解答,希望对 ...

  8. Java中的Graphics2D类基本使用教程

    Java语言在Graphics类提供绘制各种基本的几何图形的基础上,扩展Graphics类提供一个Graphics2D类,它拥用更强大的二维图形处理能力,提供.坐标转换.颜色管理以及文字布局等更精确的 ...

  9. test20190409 线段

    题意 线段(segment) [题目描述] 给定n条线段,第i条线段的左端点为\(l_i\),右端点为\(r_i\).第i条线段覆盖了点x当且仅当\(l_i ≤x ≤ r_i\). 给定Q个询问,第i ...

  10. linux前后台任务的切换以及执行暂停

    command & 把command命令放到后台执行 ctrl+z 暂停该任务,并且放到后台 jobs 查看任务 bg n 把jobs号码为n的任务放到后台执行 fg n 把jobs号码为n的 ...