当前位置: 首页 > news >正文

【BUG 记录】史诗级 BUG - MYSQL 删库删表却没有备份如何恢复数据

【BUG 记录】史诗级 BUG - MYSQL 删库删表却没有备份如何恢复数据

  • 1. 问题描述
  • 2. 解决方案(binlog)
    • 2.1 构造测试环境
    • 2.2 查看 MySQL 环境是否开启 binlog
    • 2.3 查看所有的 binlog 日志记录
    • 2.4 查看当前正在使用的是哪一个 binlog 文件
    • 2.5 查看此时的 binlog 日志记录
    • 2.6 删除记录(这里模拟删除表操作)
    • 2.7 mysqlbinlog 恢复数据
    • 2.8 binlog2sql 数据回滚
  • 3. binlog2sql 详细介绍
    • 3.1 概念
    • 3.2 安装 binlog2sql
    • 3.3 更改mysql配置文件
    • 3.4 运行脚本获取数据库里的内容
    • 3.5 删除数据
    • 3.6 回滚数据

在这里插入图片描述

1. 问题描述


  在实际开发过程中,不小心把测试环境的三张业务表(usercomanyuser_dept_rel)数据删除了,并且当时没有备份这三张表的数据,导致测试环境瘫痪,其中最关键的user表给删了,导致很多测试账号登录不上系统,搞了一下午,最终通过binlog日志 恢复了数据。在此记录一下当时的解决过程。

环境:Python 3.11
mysql:8.0+

2. 解决方案(binlog)

2.1 构造测试环境


  1. 库(test

  2. 建表(usercompanyuser_dept_rel),面下测试中主要使用user表来演示

    create table user(id int AUTO_INCREMENT primary key,name varchar(50),phone varchar(50)
    );insert into user(id,name,phone) value(1,"zhangsan",17756566565);
    insert into user(id,name,phone) value(2,"lisi",17712345687);
    insert into user(id,name,phone) value(3,"wangwu",17756562333);
    insert into user(id,name,phone) value(4,"zhaoliu",17756564444);create table company(id int AUTO_INCREMENT primary key,name varchar(50),code varchar(50)
    );insert into company(id,name,code) value(1,"测试公司1","ceshigongsi1");
    insert into company(id,name,code) value(2,"测试公司2","ceshigongsi2");
    insert into company(id,name,code) value(3,"测试公司3","ceshigongsi3");
    insert into company(id,name,code) value(4,"测试公司4","ceshigongsi4");create table user_dept_rel(id int AUTO_INCREMENT primary key,user_id int,dept_id int
    );insert into user_dept_rel(id,user_id,dept_id) value(1,111,1001);
    insert into user_dept_rel(id,user_id,dept_id) value(2,222,1002);
    insert into user_dept_rel(id,user_id,dept_id) value(3,333,1003);
    insert into user_dept_rel(id,user_id,dept_id) value(4,444,1004);
    
  3. 查看数据
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

2.2 查看 MySQL 环境是否开启 binlog


  通过命令查看是否开启 binlog 记录功能(该功能,默认是开启的)

show variables like '%log_bin%';

在这里插入图片描述

2.3 查看所有的 binlog 日志记录


  1. 命令
    show binary logs;
    
  2. 结果
    在这里插入图片描述

2.4 查看当前正在使用的是哪一个 binlog 文件


  1. 命令
    -- 查看当前的 binlog 文件
    show master status;
    
  2. 结果
    在这里插入图片描述
    当使用的是MYSQL-bin.000042这个日志文件,在前面 binlog 日志文件路径 (我的是D:\Settings\MySQL\DATA\Data )下查看
    在这里插入图片描述

2.5 查看此时的 binlog 日志记录


  1. 命令

    show binlog events in 'MYSQL-bin.000042';
    
  2. 结果

    在这里插入图片描述

2.6 删除记录(这里模拟删除表操作)


  1. 删除
    delete from test.user;
    delete from test.company;
    delete from test.user_dept_rel;
    
  2. 查看日志
    在这里插入图片描述
    可以看到我们三个删除事件,注意binlog 中每个事件都有一个 begincommit,我们后面进行恢复或回滚的时候开始和结束的 pos 都是取的事件整体的开始点和结束点。比如上面第一个删除事件的开始点(Pos)其实是7868,结束点(End_log_pos)则是8167

2.7 mysqlbinlog 恢复数据


注意: 这里是恢复数据,不是回滚数据

  • 恢复的本质:将原有的插入语句再执行一遍
  • 回滚的本质:回退到删除之前的状态

  mysqlbinlogmysql自带的命令,一般是在mysql安装目录下的bin目录里。因为我们是恢复数据,所以要找到已经删除语句的对应写入事件,将该事件再重新执行一遍即可

  1. 通过命令确认插入语句的事件位置

    show binlog events in 'MYSQL-bin.000042';
    

    在这里插入图片描述
    确定了 user 表的插入事件起始位置是3677 ,结果位置是4819

  2. 使用 mysqlbinlog 命令恢复

    D:\Settings\MySQL\DATA\Data>mysqlbinlog.exe --start-position=3677 --stop-position=4819 mysql-bin.000042 | mysql -uroot -p123456
    

    常用参数解释
    D:\Settings\MySQL\DATA\Data> : mysqlbinlog 的路径,就上面通过语句查出来的日志路径
    --start-position:起始位置
    --stop-position:结束位置
    --start-datetime:起始日期
    --stop-datetime:结束日期
    mysql-bin.000042:日志文件

  3. 查看结果
    在这里插入图片描述

    • 查看表:在这里插入图片描述
      可以看到数据已经回来了

    • 查看 binlog 日志:
      在这里插入图片描述
      可以看出,日志中也多了四条写入事件。这里只恢复了user表,companyuser_dept 同理

注意如果说找不到 insert 语句了,或者insert 语句 在很久之前执行的,找不到,我们可以通过在日志中找出它的 delete 语句,然后手动将 delete from 语句转换成 insert 语句(可以写一个python 脚本),重新执行一下就可以了。(我当时就是这么干的)

小结

  • mysqlbinlog命令只是用于恢复,不能用于回滚。如果数据进行update操作,则很难通过该命令恢复。所以该命令比较适用一些数据迁移,数据同步的场景。

  • mysqlbinlog 运行过程中如果出现unknown variable 'default-character-set=utf8mb4'异常,可以再该命令后加--no-defaults参数解决:mysqlbinlog --no-defaults

2.8 binlog2sql 数据回滚


  binlog2sql是一个第三方的工具,binlog2sql回滚的原理是生成要回滚事件对应的sql语句,我们最后只需要拷贝该语句实现即可。

3. binlog2sql 详细介绍

3.1 概念


  binlog2sql是一个用于解析二进制日志的开源工具。它具有从二进制日志中提取原始 SQL 语句的功能。它具有从二进制日志生成回滚 SQL 以进行时间点恢复的功能。

3.2 安装 binlog2sql


前提条件:安装这个工具,需要电脑具备python环境,如果没有还请自行安装python 3.11 (这块我的python版本是3.11)

cd D:\Workspace\PyCharm\
git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql
pip install -r requirements.txt

3.3 更改mysql配置文件


  在 my.ini 中添加,找到log-bin="MYSQL-bin",然后在下一行添加

log-bin
server_id = 1
binlog_format = row
binlog_row_image = full

在这里插入图片描述

~修改完之后重启 mysql 服务

3.4 运行脚本获取数据库里的内容


D:\Workspace\PyCharm\binlog2sql>cd binlog2sql
D:\Workspace\PyCharm\binlog2sql\binlog2sql>python binlog2sql.py -uroot -p123456 -P3306 -d test -t user --start-file=D:\Settings\MySQL\DATA\Data\  --stop-file=MYSQL-bin.000042
Traceback (most recent call last):File "D:\Workspace\PyCharm\binlog2sql\binlog2sql\binlog2sql.py", line 145, in <module>binlog2sql = Binlog2sql(connection_settings=conn_setting, start_file=args.start_file, start_pos=args.start_pos,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^File "D:\Workspace\PyCharm\binlog2sql\binlog2sql\binlog2sql.py", line 46, in __init__self.connection = pymysql.connect(**self.conn_setting)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^File "D:\Settings\Python\Python311\Lib\site-packages\pymysql\__init__.py", line 90, in Connectreturn Connection(*args, **kwargs)^^^^^^^^^^^^^^^^^^^^^^^^^^^File "D:\Settings\Python\Python311\Lib\site-packages\pymysql\connections.py", line 706, in __init__self.connect()File "D:\Settings\Python\Python311\Lib\site-packages\pymysql\connections.py", line 931, in connectself._get_server_information()File "D:\Settings\Python\Python311\Lib\site-packages\pymysql\connections.py", line 1269, in _get_server_informationself.server_charset = charset_by_id(lang).name^^^^^^^^^^^^^^^^^^^File "D:\Settings\Python\Python311\Lib\site-packages\pymysql\charset.py", line 38, in by_idreturn self._by_id[id]~~~~~~~~~~~^^^^
KeyError: 255

  如果出现以上报错,更新PyMySQL 即可解决,mysql8的版本问题,安装对应的binlog2sql工具版本

pip uninstall PyMySQL
pip install PyMySQL==0.9.3

再次运行

D:\Workspace\PyCharm\binlog2sql\binlog2sql>python binlog2sql.py -uroot -p123456 -P3306 -d test -t user --start-file=MYSQL-bin.000042  --stop-file=MYSQL-bin.000042
D:\Settings\Python\Python311\Lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 1")result = self._query(query)
USE b'test';
create table t1 (title varchar(100) default null,price int not null) engine=innodb;
USE b'test';
create table user(id int AUTO_INCREMENT primary key,name varchar(50),phone varchar(50)
);
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (1, 'zhangsan', '17756566565'); #start 3598 end 3874 time 2024-02-26 10:55:04
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (2, 'lisi', '17712345687'); #start 3905 end 4177 time 2024-02-26 10:55:04
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (3, 'wangwu', '17756562333'); #start 4208 end 4482 time 2024-02-26 10:55:04
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (4, 'zhaoliu', '17756564444'); #start 4513 end 4788 time 2024-02-26 10:55:04
USE b'test';
create table company(id int AUTO_INCREMENT primary key,name varchar(50),code varchar(50)
);
USE b'test';
create table user_dept_rel(id int AUTO_INCREMENT primary key,user_id int,dept_id int
);
DELETE FROM `test`.`user` WHERE `id`=1 AND `name`='zhangsan' AND `phone`='17756566565' LIMIT 1; #start 7789 end 8136 time 2024-02-26 11:39:35
DELETE FROM `test`.`user` WHERE `id`=2 AND `name`='lisi' AND `phone`='17712345687' LIMIT 1; #start 7789 end 8136 time 2024-02-26 11:39:35
DELETE FROM `test`.`user` WHERE `id`=3 AND `name`='wangwu' AND `phone`='17756562333' LIMIT 1; #start 7789 end 8136 time 2024-02-26 11:39:35
DELETE FROM `test`.`user` WHERE `id`=4 AND `name`='zhaoliu' AND `phone`='17756564444' LIMIT 1; #start 7789 end 8136 time 2024-02-26 11:39:35
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (1, 'zhangsan', '17756566565'); #start 9210 end 9497 time 2024-02-26 10:55:04
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (2, 'lisi', '17712345687'); #start 9528 end 9807 time 2024-02-26 10:55:04
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (3, 'wangwu', '17756562333'); #start 9838 end 10119 time 2024-02-26 10:55:04
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (4, 'zhaoliu', '17756564444'); #start 10150 end 10432 time 2024-02-26 10:55:04

从以上内容能看出来我们刚才所执行的语句,该命令只是将二进制文件解析成我们可读的sql文件。

常用参数解释

-h 主机
-P 端口 
-u 用户名
-p 密码
-d 指定库名
-t 指定表--start-file   起始解析文件,只需文件名,无需全路径
--start-datetime 起始解析时间,格式'%Y-%m-%d %H:%M:%S'。可选。默认不过滤。
--stop-datetime 终止解析时间,格式'%Y-%m-%d %H:%M:%S'。可选。默认不过滤。--only-dml 只解析dml,忽略ddl。可选。默认False。
--sql-type 只解析指定类型,支持INSERT, UPDATE, DELETE。多个类型用空格隔开,如--sql-type INSERT DELETE。可选。默认为增删改都解析。用了此参数但没填任何类型,则三者都不解析。

其他参数大家可以自己网上搜索

3.5 删除数据


  由于我们刚才重启了mysql服务,所以当前的binlog日志文件变了
在这里插入图片描述
  现在日志文件是MYSQL-bin.000044

  我们删除数据(这块只删除user表)

delete from user;

  查看日志文件,会多一条delete事件

在这里插入图片描述

3.6 回滚数据


  添加参数 --flashback,生成rollback语句

# 这是没有--flashback 的执行结果,将delete from 语句打印出来
D:\Settings\Python\Python311\Lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 1")result = self._query(query)
DELETE FROM `test`.`user` WHERE `id`=1 AND `name`='zhangsan' AND `phone`='17756566565' LIMIT 1; #start 4 end 504 time 2024-02-26 14:22:35
DELETE FROM `test`.`user` WHERE `id`=2 AND `name`='lisi' AND `phone`='17712345687' LIMIT 1; #start 4 end 504 time 2024-02-26 14:22:35
DELETE FROM `test`.`user` WHERE `id`=3 AND `name`='wangwu' AND `phone`='17756562333' LIMIT 1; #start 4 end 504 time 2024-02-26 14:22:35
DELETE FROM `test`.`user` WHERE `id`=4 AND `name`='zhaoliu' AND `phone`='17756564444' LIMIT 1; #start 4 end 504 time 2024-02-26 14:22:35# 这是添加--flashback 的执行结果,将delete from 语句转换成了insert into 语句 打印出来
D:\Workspace\PyCharm\binlog2sql\binlog2sql>python binlog2sql.py -uroot -p123456 -P3306 -d test -t user --start-file=MYSQL-bin.000044 --flashback
D:\Settings\Python\Python311\Lib\site-packages\pymysql\cursors.py:170: Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 1")result = self._query(query)
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (4, 'zhaoliu', '17756564444'); #start 4 end 504 time 2024-02-26 14:22:35
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (3, 'wangwu', '17756562333'); #start 4 end 504 time 2024-02-26 14:22:35
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (2, 'lisi', '17712345687'); #start 4 end 504 time 2024-02-26 14:22:35
INSERT INTO `test`.`user`(`id`, `name`, `phone`) VALUES (1, 'zhangsan', '17756566565'); #start 4 end 504 time 2024-02-26 14:22:35

  我们复制insert into 语句执行就可以了!!!

相关文章:

  • DS注解作用
  • http状态,cookie、session、token的对比
  • COMSOL传热建模
  • etcd java 客户端jetcd库踩坑日志
  • 【HDFS】Decommision(退役) EC数据节点剩最后几个块卡住的问题
  • 超低功耗32位单片机MM32L0130
  • 【Android】隐藏settings中的二级菜单
  • 【Kubernetes】k8s中容器之间、pod之间如何进行网络通信?
  • 【比较mybatis、lazy、sqltoy、mybatis-flex操作数据】操作批量新增、分页查询(二)
  • 计算机网络(2)-----数据链路层
  • 集合篇之ArrayList
  • 【软件测试】--功能测试4-html介绍
  • untiy 室内灯光最佳实践
  • 爬取博客的图片并且将它存储到响应的目录
  • C#高级:DataGridView的详解
  • C语言笔记(第一章:C语言编程)
  • gops —— Go 程序诊断分析工具
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • isset在php5.6-和php7.0+的一些差异
  • Java 多线程编程之:notify 和 wait 用法
  • Laravel Mix运行时关于es2015报错解决方案
  • Laravel 菜鸟晋级之路
  • learning koa2.x
  • PermissionScope Swift4 兼容问题
  • STAR法则
  • Vue 动态创建 component
  • zookeeper系列(七)实战分布式命名服务
  • 百度小程序遇到的问题
  • 多线程事务回滚
  • 关于List、List?、ListObject的区别
  • 全栈开发——Linux
  • 实现菜单下拉伸展折叠效果demo
  • 由插件封装引出的一丢丢思考
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #pragma pack(1)
  • #Z2294. 打印树的直径
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (06)金属布线——为半导体注入生命的连接
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (原)本想说脏话,奈何已放下
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .net 托管代码与非托管代码
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET使用存储过程实现对数据库的增删改查
  • .Net小白的大学四年,内含面经
  • .Net中wcf服务生成及调用
  • /bin/rm: 参数列表过长"的解决办法