欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

通过binlog恢复误删除的数据(一)

程序员文章站 2022-03-09 14:25:43
...

1.1 故障说明

01:生产环境上,使用delete命令删除了一条记录(数据),条件指定错球了;
02:生产环境上是开启了binlog日志的,模式为row模式;
03:需要马上进行数据的恢复;


1.2 数据库环境

## 数据库版本、binlog的配置
mysql> select @@version,@@global.log_bin,@@global.log_bin_basename,@@binlog_format;
+------------+------------------+--------------------------------------+-----------------+
| @@version  | @@global.log_bin | @@global.log_bin_basename            | @@binlog_format |
+------------+------------------+--------------------------------------+-----------------+
| 5.7.28-log |                1 | /mysql/logs/3306/binlog/21_mysql_bin | ROW             |
+------------+------------------+--------------------------------------+-----------------+
1 row in set (0.00 sec)


## binlog的其它配置
mysql> select @@global.binlog_row_image,@@binlog_rows_query_log_events;
+---------------------------+--------------------------------+
| @@global.binlog_row_image | @@binlog_rows_query_log_events |
+---------------------------+--------------------------------+
| FULL                      |                              1 |
+---------------------------+--------------------------------+
1 row in set (0.00 sec)


## 特别说明
binlog_row_image 
  # 默认值是full,针对row模式的binlog,对inset\update\delete语句进行详细的记录
  
binlog_rows_query_log_events
  # 默认值是off,针对row模式,可在线设置,若为on可以在mysql中用show binlog events in "二进制日志文件名";
  # 命令查看二进制日志文件中记录的DML语句和其它信息;


## 当前binlog日志的状态
mysql> show master status\G
*************************** 1. row ***************************
             File: 21_mysql_bin.000001
         Position: 154
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 
1 row in set (0.00 sec)

1.3 源数据模拟

## 创建lili库,并进入到lili库下面
create database if not exists lili character set utf8 collate utf8_general_ci;
use lili;

## 创建test1表
create table if not exists test1(
  id int unsigned not null auto_increment comment"序列",
  name varchar(20) not null comment"姓名",
  sex varchar(10) not null comment"性别",
  age tinyint unsigned not null comment"年龄",
  primary key(id)
)engine=innodb character set utf8 collate utf8_general_ci comment"测试表1";

    ## 注意test1表的id字段是auto_increment(自增)的,且有主键索引;
  
## 往test1表中插入几条数据
insert into test1(name,sex,age) values
("chenliang01","男",21),
("chenliang02","男",22),
("chenliang03","男",23),
("chenliang04","男",24),
("chenliang05","男",25),
("chenliang06","男",26),
("chenliang07","男",27),
("chenliang08","男",28),
("chenliang09","男",29),
("chenliang10","男",30);
commit;

## 查看test1表中的数据
select * from test1;

1.4 故障模拟

删除test1表中id等于10的记录

mysql> select * from test1 where id=10;   # 查看test1表中id等于10的记录
+----+-------------+-----+-----+
| id | name        | sex | age |
+----+-------------+-----+-----+
| 10 | chenliang10 ||  30 |
+----+-------------+-----+-----+
1 row in set (0.00 sec)

mysql> delete from test1 where id=10;      # 删除test1表中id等于10的记录
Query OK, 1 row affected (0.00 sec)

mysql> select * from test1 where id=10;    # 查看test1表中id等于10的记录是否删除成功
Empty set (0.00 sec)

1.5 解决思路

01:在得知使用delete语句误删除了数据,这里是一条记录哈;需要做的事情:
    A:使用show master status;命令查看看当前binlog是哪个文件,并记录下来;
    B:执行flush log命令重新生成新的binlog日志文件来记录sql语句产生的记录;
    C:确定删除数据的命令是什么,这里是:delete from test1 where id=10;
    
02:要不要考虑锁表呢,这里就不用了,这里因为是只删除了一条数据而已;

03:对01阶段中的A步骤中的binlog日志做备份(复制一份到其它目录下,防止损坏源binlog文件);

04:在mysql中使用"show binlog events in "01阶段中A步骤记录的binlog文件名";"命令找到
    delete from test1 where id=10;这条语句(事务)的起始pos点和结束pos点;
    
05:用mysqlbinlog命令结合04阶段找到的起始pos点和结束pos点对03步骤复制的binlog文件
    进行解释并生成新的文件;
    
06:在05阶段中新生成的文件中进行处理,这里要结合到test1表的表结构来进行处理哈;  ****这里最重要了,要多想

1.6 故障处理步骤

解决思路中的01阶段

## 查看当前binlog日志文件是哪个
mysql> show master status\G
*************************** 1. row ***************************
             File: 21_mysql_bin.000001     # 当前的binlog日志文件
         Position: 1967                    # 当前binlog日志文件的pos点
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 
1 row in set (0.00 sec)

## 使用flush logs命令重新生成新的binlog命令
mysql> show master status\G
*************************** 1. row ***************************
             File: 21_mysql_bin.000002
         Position: 154
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 
1 row in set (0.00 sec)

解决思路中的03阶段

[aaa@qq.com ~]# cp -a /mysql/logs/3306/binlog/21_mysql_bin.000001 /tmp/
[aaa@qq.com ~]# ll /tmp/21_mysql_bin.000001 
-rw-r----- 1 mysql mysql 2017 3月   3 19:22 /tmp/21_mysql_bin.000001

解决思路中的04阶段
通过binlog恢复误删除的数据(一)
最终的结果:起始pos点1699;结束pos点1967


解决思路中的05阶段

## 进入到之前备份binlog文件的目录
[aaa@qq.com tmp]# pwd
/tmp
[aaa@qq.com tmp]# ll 21_mysql_bin.000001 
-rw-r----- 1 mysql mysql 2017 3月   3 19:22 21_mysql_bin.000001

## 对binlog日志进行解释并保存到a.txt文件中
[aaa@qq.com tmp]# mysqlbinlog --base64-output=decode-rows -vv --start-position=1699 --stop-position=1967 21_mysql_bin.000001 >a.txt

看一看a.txt这个文件,后面对这个文件一步一步的处理
通过binlog恢复误删除的数据(一)

解决思路中的06阶段

## 处理a.txt文件,生成b.txt文件
[aaa@qq.com tmp]# sed -n '/###/'p a.txt 
### DELETE FROM `lili`.`test1`
### WHERE
###   @1=10 /* INT meta=0 nullable=0 is_null=0 */
###   @2='chenliang10' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
###   @3='男' /* VARSTRING(30) meta=30 nullable=0 is_null=0 */
###   @4=30 /* TINYINT meta=0 nullable=0 is_null=0 */
[aaa@qq.com tmp]#
[aaa@qq.com tmp]# sed -n '/###/'p a.txt >b.txt


## 处理b.txt文件,生成c.txt文件
[aaa@qq.com tmp]# sed 's/### //g' b.txt 
DELETE FROM `lili`.`test1`
WHERE
  @1=10 /* INT meta=0 nullable=0 is_null=0 */
  @2='chenliang10' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
  @3='男' /* VARSTRING(30) meta=30 nullable=0 is_null=0 */
  @4=30 /* TINYINT meta=0 nullable=0 is_null=0 */
[aaa@qq.com tmp]#
[aaa@qq.com tmp]# sed 's/### //g' b.txt  >c.txt


## 处理c.txt文件生成d.txt文件
[aaa@qq.com tmp]# sed 's#/.*#,#g' c.txt 
DELETE FROM `lili`.`test1`
WHERE
  @1=10 ,
  @2='chenliang10' ,
  @3='男' ,
  @4=30 , 
[aaa@qq.com tmp]# 
[aaa@qq.com tmp]# sed 's#/.*#,#g' c.txt >d.txt


## 处理d.txt文件,生成e.txt
[aaa@qq.com tmp]# sed -r 's#(@4.*),#\1;#g' d.txt 
DELETE FROM `lili`.`test1`
WHERE
  @1=10 ,
  @2='chenliang10' ,
  @3='男' ,
  @4=30 ;    
[aaa@qq.com tmp]# 
[aaa@qq.com tmp]# sed -r 's#(@4.*),#\1;#g' d.txt  >e.txt


## 处理e.txt文件,生成f.txt文件
[aaa@qq.com tmp]# sed 's#DELETE FROM `lili`.`test1`#INSERT INTO lili.test1#g' e.txt 
INSERT INTO lili.test1
WHERE
  @1=10 ,
  @2='chenliang10' ,
  @3='男' ,
  @4=30 ;
[aaa@qq.com tmp]#
[aaa@qq.com tmp]# sed 's#DELETE FROM `lili`.`test1`#INSERT INTO lili.test1#g' e.txt >f.txt


## 处理f.txt文件,生成h.txt
[aaa@qq.com tmp]# sed 's#WHERE#SELECT#g' f.txt 
INSERT INTO lili.test1
SELECT
  @1=10 ,
  @2='chenliang10' ,
  @3='男' ,
  @4=30 ;
[aaa@qq.com tmp]# sed 's#WHERE#SELECT#g' f.txt >h.txt


## 查看test1表结构后,处理h.txt文件生成i.txt文件
mysql> desc test1;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| id    | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |   # 自增,主键,不能有重复值
| name  | varchar(20)         | NO   |     | NULL    |                |
| sex   | varchar(10)         | NO   |     | NULL    |                |
| age   | tinyint(3) unsigned | NO   |     | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

[aaa@qq.com tmp]# sed 's#INSERT INTO lili.test1#INSERT INTO lili.test1(name,sex,age)#g' h.txt 
INSERT INTO lili.test1(name,sex,age)
SELECT
  @1=10 ,
  @2='chenliang10' ,
  @3='男' ,
  @4=30 ;
[aaa@qq.com tmp]# 
[aaa@qq.com tmp]# sed 's#INSERT INTO lili.test1#INSERT INTO lili.test1(name,sex,age)#g' h.txt  >i.txt


## 处理i.txt文件生成j.txt文件
[aaa@qq.com tmp]# sed '/@1=.*/'d i.txt
INSERT INTO lili.test1(name,sex,age)
SELECT
  @2='chenliang10' ,
  @3='男' ,
  @4=30 ;
[aaa@qq.com tmp]# sed '/@1=.*/'d i.txt >j.txt


## 处理j.txt文件,生成aa.sql
[aaa@qq.com tmp]# sed 's#@[1-9]=##g' j.txt 
INSERT INTO lili.test1(name,sex,age)
SELECT
  'chenliang10' ,
  '男' ,
  30 ;
[aaa@qq.com tmp]# 
[aaa@qq.com tmp]# 
[aaa@qq.com tmp]# sed 's#@[1-9]=##g' j.txt >aa.sql


## 在aa.sql文件的尾部追加commit;内容
[aaa@qq.com tmp]# echo "commit;" >>aa.sql 
[aaa@qq.com tmp]# cat aa.sql 
INSERT INTO lili.test1(name,sex,age)
SELECT
  'chenliang10' ,
  '男' ,
  30 ;
commit;


## 将aa.sql文件中的语句在数据库中进行执行
mysql> INSERT INTO lili.test1(name,sex,age)
    -> SELECT
    ->   'chenliang10' ,
    ->   '男' ,
    ->   30 ;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from lili.test1 where name="chenliang10";
+----+-------------+-----+-----+
| id | name        | sex | age |
+----+-------------+-----+-----+
| 11 | chenliang10 ||  30 |
+----+-------------+-----+-----+
1 row in set (0.00 sec)