「删库跑路」使用Binlog日志恢复误删的MySQL数据
csdh11 2025-01-02 15:31 26 浏览
前言
“删库跑路”是程序员经常谈起的话题,今天,我就要教大家如何删!库!跑!路!
开个玩笑,今天文章的主题是如何使用Mysql内置的Binlog日志对误删的数据进行恢复,读完本文,你能够了解到:
- MySQL的binlog日志是什么?通常是用来干什么的?
- 模拟一次误删数据的操作,并且使用binlog日志恢复误删的数据。
写这篇文章的初衷,是有一次我真的险些把测试数据库的一张表给删除了,当时吓出一身冷汗。原因是由于Spring JPA的配置中,有一个spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop,其用途是每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。,这个可不能随便配置上去,直接就把你原来存在的表给drop了!
好了,回归正题,这篇文章就是想让大家放心,MySQL就算进行了误删操作,也基本都能够抢救回来。尤其是大公司内,数据可不是你想删就能删掉的,有无数权限/备份阻拦着你。
正文
Binlog介绍
binlog是记录所有数据库表结构变更(例如CREATE、ALTER TABLE…)以及表数据修改(INSERT、UPDATE、DELETE…)的二进制日志。
binlog不会记录SELECT和SHOW这类操作,因为这类操作对数据本身并没有修改,但你可以通过查询通用日志来查看MySQL执行过的所有语句。
看了上面binlog的定义,大家也应该能大致推理出binlog的三大用途:
- 恢复数据:今天要说的重点
- 数据库复制:主从数据库是通过将binlog传给从库,从库有两个线程,一个I/O线程,一个SQL线程,I/O线程读取主库传过来的binlog内容并写入到relay log,SQL线程从relay log里面读取内容,写入从库的数据库。
- 审计:用户可以通过二进制日志中的信息来进行审计,判断是否有对数据库进行注入攻击。
所以说,想要能够恢复数据,首先,你得打开Mysql的binlog,在平常你自己安装的单机Mysql中,默认情况下不会开启。下面就一步步地实践下如何开启你服务器上的Binlog日志。
在MySQL中开启Binlog
首先进入数据库控制台,运行指令:
mysql>?show?variables?like'log_bin%';
+---------------------------------+-------+
|?Variable_name???????????????????|?Value?|
+---------------------------------+-------+
|?log_bin?????????????????????????|?OFF???|
|?log_bin_basename????????????????|???????|
|?log_bin_index???????????????????|???????|
|?log_bin_trust_function_creators?|?OFF???|
|?log_bin_use_v1_row_events???????|?OFF???|
+---------------------------------+-------+
5?rows?in?set?(0.00?sec)
可以看到我们的binlog是关闭的,都是OFF。接下来我们需要修改Mysql配置文件,执行命令:
sudo?vi?/etc/mysql/mysql.conf.d/mysqld.cnf
在文件末尾添加:
log-bin=/var/lib/mysql/mysql-bin
保存文件,重启mysql服务:
sudo?service?mysql?restart
重启完成后,查看下mysql的状态:
systemctl?status?mysql.service
这时,如果你的mysql版本在5.7或更高版本,就会报错:
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.190791Z?0?[Warning]?Changed?limits:?max_open_files:?1024?(requested?5000)
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.190839Z?0?[Warning]?Changed?limits:?table_open_cache:?431?(requested?2000)
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.359713Z?0?[Warning]?TIMESTAMP?with?implicit?DEFAULT?value?is?deprecated.?Please?use?--explicit_defaults_for_timestamp?server?option?(se
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.361395Z?0?[Note]?/usr/sbin/mysqld?(mysqld?5.7.28-0ubuntu0.16.04.2-log)?starting?as?process?5930?...
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.363017Z?0?[ERROR]?You?have?enabled?the?binary?log,?but?you?haven't?provided?the?mandatory?server-id.?Please?refer?to?the?proper?server
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.363747Z?0?[ERROR]?Aborting
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.363922Z?0?[Note]?Binlog?end
Jan?06?15:49:58?VM-0-11-ubuntu?mysqld[5930]:?2020-01-06T07:49:58.364108Z?0?[Note]?/usr/sbin/mysqld:?Shutdown?complete
Jan?06?15:49:58?VM-0-11-ubuntu?systemd[1]:?mysql.service:?Main?process?exited,?code=exited,?status=1/FAILURE
You have enabled the binary log, but you haven't provided the mandatory server-id. Please refer to the proper server
之前我们的配置,对于5.7以下版本应该是可以的。但对于高版本,我们需要指定server-id。
如果你不是分布式的部署Mysql,这个server-id随机给个数字就可以。
server-id=123454
模拟删除数据并恢复
- 首先新建数据库mytest,新建一张表table1,结构见下方SQL代码
CREATE?DATABASE?`test`?;
USE?`test`;
DROP?TABLE?IF?EXISTS?`table1`;
CREATE?TABLE?`table2`?(
??`id`?int(11)?DEFAULT?NULL,
??`name`?varchar(20)?DEFAULT?NULL
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;
- 插入两条数据,分别是 (1,'A'),(2,'B')
INSERT?INTO?`table1`?VALUES?(1,'A'),(2,'B');
- 我们看一下binlog日志的状态,使用show master status
mysql>?show?master?status
????->?;
+------------------+----------+--------------+------------------+-------------------+
|?File?????????????|?Position?|?Binlog_Do_DB?|?Binlog_Ignore_DB?|?Executed_Gtid_Set?|
+------------------+----------+--------------+------------------+-------------------+
|?mysql-bin.000001?|??????690?|??????????????|??????????????????|???????????????????|
+------------------+----------+--------------+------------------+-------------------+
1?row?in?set
binlog日志特征:每当我们重启MySQL一次,会自动生成一个binlog文件,当然,我们也可以手动的来刷新binlog文件,通过 flush logs,同样会新创建一个binlog文件。实际上当服务器在重启时,也会调用flush logs操作。
上图代码中可以看到,现在我们正在使用 mysql-bin.0000001 ,并且这个文件现在正在记录到690行。
- 然后,使用flush logs来主动刷新一次binlog
mysql>?flush?logs;
Query?OK,?0?rows?affected
mysql>?show?master?status
????->?;
+------------------+----------+--------------+------------------+-------------------+
|?File?????????????|?Position?|?Binlog_Do_DB?|?Binlog_Ignore_DB?|?Executed_Gtid_Set?|
+------------------+----------+--------------+------------------+-------------------+
|?mysql-bin.000002?|??????154?|??????????????|??????????????????|???????????????????|
+------------------+----------+--------------+------------------+-------------------+
1?row?in?set
可以看到,现在日志文件在 mysql-bin.000002 文件中,位置为154。也就是我们主动刷新了一次binlog,生成了新的000002,而000001则已经归档了,不会再写入新的日志进去了。
- 接下来我们在插入两条数据
insert?into?table1?values?(3,'C');
insert?into?table1?values?(4,'D');
mysql>?select?*?from?table1;
+----+----+
|?id?|name|
+----+----+
|??1?|?A??|
|??2?|?B??|
|??3?|?C??|
|??4?|?D??|
+----+----+
- 这时候我们已经有了四条数据,我们再次flush logs,把mysql-bin.000002日志存档,开启新的mysql-bin.000003日志,这样,每次我们插入的数据彼此独立。实际情况下,binlog会比较复杂,这里也是做了简化,为了理解更方便。
mysql>?flush?logs;
Query?OK,?0?rows?affected
mysql>?show?master?status;
+------------------+----------+--------------+------------------+-------------------+
|?File?????????????|?Position?|?Binlog_Do_DB?|?Binlog_Ignore_DB?|?Executed_Gtid_Set?|
+------------------+----------+--------------+------------------+-------------------+
|?mysql-bin.000003?|??????154?|??????????????|??????????????????|???????????????????|
+------------------+----------+--------------+------------------+-------------------+
1?row?in?set
- 然后我们删除id为4的数据(4,D),并且再次刷新binlog,如此一来,binlog.000003里面只有一条删除操作。
mysql>?delete?from?table1?where?id?=?4;
Query?OK,?1?row?affected
mysql>?show?master?status;
+------------------+----------+--------------+------------------+-------------------+
|?File?????????????|?Position?|?Binlog_Do_DB?|?Binlog_Ignore_DB?|?Executed_Gtid_Set?|
+------------------+----------+--------------+------------------+-------------------+
|?mysql-bin.000003?|??????423?|??????????????|??????????????????|???????????????????|
+------------------+----------+--------------+------------------+-------------------+
1?row?in?set
mysql>?flush?logs;
Query?OK,?0?rows?affected
mysql>?show?master?status;
+------------------+----------+--------------+------------------+-------------------+
|?File?????????????|?Position?|?Binlog_Do_DB?|?Binlog_Ignore_DB?|?Executed_Gtid_Set?|
+------------------+----------+--------------+------------------+-------------------+
|?mysql-bin.000004?|??????154?|??????????????|??????????????????|???????????????????|
+------------------+----------+--------------+------------------+-------------------+
1?row?in?set
- 让我们来好好观察下mysql-bin.00002和mysql-bin00003两个binlog,使用命令:show binlog events in 'mysql-bin.000003'
mysql>?show?binlog?events?in?'mysql-bin.000003';
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
|?Log_name?????????|?Pos?|?Event_type?????|?Server_id?|?End_log_pos?|?Info???????????????????????????????????????????????????|
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
|?mysql-bin.000003?|???4?|?Format_desc????|????123456?|?????????123?|?Server?ver:?5.7.28-0ubuntu0.16.04.2-log,?Binlog?ver:?4?|
|?mysql-bin.000003?|?123?|?Previous_gtids?|????123456?|?????????154?|????????????????????????????????????????????????????????|
|?mysql-bin.000003?|?154?|?Anonymous_Gtid?|????123456?|?????????219?|?SET?@@SESSION.GTID_NEXT=?'ANONYMOUS'???????????????????|
|?mysql-bin.000003?|?219?|?Query??????????|????123456?|?????????293?|?BEGIN??????????????????????????????????????????????????|
|?mysql-bin.000003?|?293?|?Table_map??????|????123456?|?????????343?|?table_id:?108?(test.table1)??????????????????????????????|
|?mysql-bin.000003?|?343?|?Delete_rows????|????123456?|?????????392?|?table_id:?108?flags:?STMT_END_F????????????????????????|
|?mysql-bin.000003?|?392?|?Xid????????????|????123456?|?????????423?|?COMMIT?/*?xid=39?*/????????????????????????????????????|
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
7?rows?in?set
mysql>?show?binlog?events?in?'mysql-bin.000002';
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
|?Log_name?????????|?Pos?|?Event_type?????|?Server_id?|?End_log_pos?|?Info???????????????????????????????????????????????????|
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
|?mysql-bin.000002?|???4?|?Format_desc????|????123456?|?????????123?|?Server?ver:?5.7.28-0ubuntu0.16.04.2-log,?Binlog?ver:?4?|
|?mysql-bin.000002?|?123?|?Previous_gtids?|????123456?|?????????154?|????????????????????????????????????????????????????????|
|?mysql-bin.000002?|?154?|?Anonymous_Gtid?|????123456?|?????????219?|?SET?@@SESSION.GTID_NEXT=?'ANONYMOUS'???????????????????|
|?mysql-bin.000002?|?219?|?Query??????????|????123456?|?????????293?|?BEGIN??????????????????????????????????????????????????|
|?mysql-bin.000002?|?293?|?Table_map??????|????123456?|?????????343?|?table_id:?108?(test.table1)??????????????????????????????|
|?mysql-bin.000002?|?343?|?Write_rows?????|????123456?|?????????390?|?table_id:?108?flags:?STMT_END_F????????????????????????|
|?mysql-bin.000002?|?390?|?Xid????????????|????123456?|?????????421?|?COMMIT?/*?xid=34?*/????????????????????????????????????|
|?mysql-bin.000002?|?421?|?Anonymous_Gtid?|????123456?|?????????486?|?SET?@@SESSION.GTID_NEXT=?'ANONYMOUS'???????????????????|
|?mysql-bin.000002?|?486?|?Query??????????|????123456?|?????????560?|?BEGIN??????????????????????????????????????????????????|
|?mysql-bin.000002?|?560?|?Table_map??????|????123456?|?????????610?|?table_id:?108?(test.table1)??????????????????????????????|
|?mysql-bin.000002?|?610?|?Write_rows?????|????123456?|?????????659?|?table_id:?108?flags:?STMT_END_F????????????????????????|
|?mysql-bin.000002?|?659?|?Xid????????????|????123456?|?????????690?|?COMMIT?/*?xid=35?*/????????????????????????????????????|
|?mysql-bin.000002?|?690?|?Rotate?????????|????123456?|?????????737?|?mysql-bin.000003;pos=4?????????????????????????????????|
+------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
13?rows?in?set
虽然有很多看似复杂的指令,但是还是不难看出,在02里,有两条写操作,03里有一条删除操作。
一条插入操作的完整日志是这样:
|?mysql-bin.000002?|?154?|?Anonymous_Gtid?|????123456?|?????????219?|?SET?@@SESSION.GTID_NEXT=?'ANONYMOUS'???????????????????|
|?mysql-bin.000002?|?219?|?Query??????????|????123456?|?????????293?|?BEGIN??????????????????????????????????????????????????|
|?mysql-bin.000002?|?293?|?Table_map??????|????123456?|?????????343?|?table_id:?108?(test.table1)??????????????????????????????|
|?mysql-bin.000002?|?343?|?Write_rows?????|????123456?|?????????390?|?table_id:?108?flags:?STMT_END_F????????????????????????|
|?mysql-bin.000002?|?390?|?Xid????????????|????123456?|?????????421?|?COMMIT?/*?xid=34?*/????????????????????????????????????|
- 我们的目的是恢复误删的数据,其实就是将binlog.000002日志的两条插入记录重演一遍,而不需要取理会binlog.000003的操作(因为删除是一个误操作)
所以现在能理解为什么我们频繁刷新binlog了吧,当然,在实际的线上环境中,我们肯定需要将binlog导出后,仔细筛选出误操作,并将其排除,之后再运行binlog。
在本文中,我们只做一个恢复两条插入语句的操作,执行语句:
sudo?mysqlbinlog?/var/lib/mysql/mysql-bin.000002?--start-position?154?--stop-position?690?|?mysql?-uroot?-p?mytest
注意:这里填写的路径/var/lib/mysql/mysql-bin.000002需要具体到你的binlog目录,网上大部分文章只写到mysql-bin.000002,如果你不在目录里,mysqlbinlog命令并不会自动定位binlog所在路径。
参数描述:
--start-datetime:从二进制日志中读取指定等于时间戳或者晚于本地计算机的时间
--stop-datetime:从二进制日志中读取指定小于时间戳或者等于本地计算机的时间?取值和上述一样
--start-position:从二进制日志中读取指定position?事件位置作为开始。
--stop-position:从二进制日志中读取指定position?事件位置作为事件截至
执行成功后,再次查看表table1,可以看到两条新的id=3和4的数据被插入了进来。恢复成功了。
mysql>?select?*?from?table1;
+----+----+
|?id?|name|
+----+----+
|??1?|?A??|
|??2?|?B??|
|??3?|?C??|?
|??3?|?C??|
|??4?|?D??|
+----+----+
6?rows?in?set
延伸思考
Binlog在什么情况下无法恢复数据?
结语
删库跑路不用怕,其他开发运维都等着恢复你的数据呢,多好的练手机会是不是。
当然,看完binlog日志恢复数据的原理,希望大家以后在定期备份数据库的脚本里,也能够加上刷新binlog日志的命令,这样一旦某天丢失数据,可以将当天binlog数据单独拿出来还原,做到清晰可辨,也加快恢复效率。
参考
https://www.cnblogs.com/rjzheng/p/9721765.html
https://blog.csdn.net/king_kgh/article/details/74890381
https://www.jianshu.com/p/564fcc2b5e31
https://blog.csdn.net/king_kgh/article/details/74833539
关注我
我是一名后端开发工程师。
主要关注后端开发,数据安全,爬虫,物联网,边缘计算等方向,欢迎交流。
各大平台都可以找到我
- 微信公众号:后端技术漫谈
- Github:@qqxx6661
- CSDN:@后端技术漫谈
- 知乎:@后端技术漫谈
- 简书:@后端技术漫谈
- 掘金:@后端技术漫谈
原创博客主要内容
- 后端开发相关技术文章
- Java面试知识点复习全手册
- 设计模式/数据结构
- Leetcode/剑指offer 算法题解析
- SpringBoot/SpringCloud 入门实战系列
- 爬虫相关技术文章
- 逸闻趣事/好书分享/个人兴趣
个人公众号:后端技术漫谈
公众号:后端技术漫谈.jpg
如果文章对你有帮助,不妨收藏,投币,转发,在看起来~
相关推荐
- 阿里巴巴的DataX ETL工具的使用心得,数据库主从热备份神器
-
简介这是阿里出的一个ETL工具,其实就是把不同数据库的数据,高效的互相拷贝。做了很多底层优化,平均能达到1秒/1W条。如果有牛逼的优化,能达到1秒/9W条。一般用在半夜的数据获取,或者主从热备份。...
- Java开发环境搭建与配置,最全手册看这一篇就够了
-
Java开发环境搭建与配置,工具集合包括:Tomcat\JDK\JRE\Redis\Maven。JDK下载JDK安装包:jdk-8u161-windows-x64.exe。可以加关注私信我,提供百度网...
- JAVA安装教程——JDK安装(java 安装)
-
一个初学者的尝试与理解,欢迎广大网友的评论与指正。(纯兴趣爱好学习)。一、什么是JDKJDK,又称为JavaSDK是Java语言的软件开发工具包。JDK中包含JRE和Java开发工具包,JRE又...
- Windows和Linux环境下的JDK安装教程
-
JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...
- JAVA 8 环境安装配置(java环境怎么装)
-
一、下载这里选择的是OracleJDK,首先到Oracle官网下载JDK8,以Windows10操作系统为例,若是32位系统则下载“Windowsx86”,否则64位系统下载“Windows...
- 真的要开始用 JDK 17 了(jdk17好用吗)
-
最近在调研JDK17,并且试着将之前的一个小项目升级了一下,在测试环境跑了一段时间。...
- 在Windows 10下搭建Java环境(使用jdk-13.0.1)
-
一、初识JDK、JRE和JVM对于使用Java语言的开发者来说,在安装开发工具(Eclipse等)之前首先需要安装JDK(JavaDevelopmentKit,Java开发工具包)。它是整个JAV...
- 「是时候升级Java11了」 JDK11优势和JDK选择
-
专注于Java领域优质技术,欢迎关注作者:冷冷ggJava8商用收费从2019年1月份开始,OracleJDK开始对JavaSE8之后的版本开始进行商用收费,确切的说是8u201/20...
- 如何安装jdk(如何安装jdk1.8)
-
学习java首先要安装Java开发工具箱(JDK):要在计算机上编写和运行Java程序,需要安装Java开发工具箱(JDK)。JDK包括Java编译器(javac)和Java虚拟机(JVM)。可以从O...
- Java JDK下载安装及Windows环境变量配置
-
JavaJDK下载安装JDK是Java的开发工具包,要进行Java学习或开发之前,需先下载安装,下载地址如下:...
- JDK安装、Eclipse安装及运行环境配置
-
1、eclipse下载打开地址:http://www.eclipse.org/downloads/;根据自己机器的操作系统,页面上显示适应机器操作系统的Eclipse下载列表,也可以点击下图所示位置切...
- 宝塔面板安装jdk16 – 卸载默认的jdk1.8
-
昨天想安装一个halo博客,开始的时候一直安装不上,后来发现jdk版本不对,halo博客默认的jdk版本最低是jdk11,宝塔默认的是jdk1.8,所以这篇文章就来倒腾下如何在宝塔面板环境下卸载默认的...
- JDK1.8安装&环境变量配置(jdk安装步骤环境变量配置)
-
1、下载并安装JDK1.8链接:https://pan.baidu.com/s/1bfceFjfTQvLylu7a3T7fyg?pwd=ydtm...
- 如何在Windows10中配置java的JDK环境
-
今天给大家分享一下如何配置java的JDK环境。操作步骤如下:1.下载好jdk的安装文件,我下载的是jdk-10.0.1_windows-x64_bin.exe这个版本的安装文件;2.使用鼠标...
- 一周热门
- 最近发表
-
- 阿里巴巴的DataX ETL工具的使用心得,数据库主从热备份神器
- Java开发环境搭建与配置,最全手册看这一篇就够了
- JAVA安装教程——JDK安装(java 安装)
- Windows和Linux环境下的JDK安装教程
- JAVA 8 环境安装配置(java环境怎么装)
- 真的要开始用 JDK 17 了(jdk17好用吗)
- 在Windows 10下搭建Java环境(使用jdk-13.0.1)
- 「是时候升级Java11了」 JDK11优势和JDK选择
- Java近期新闻:JDK 24 RC1、JDK Mission Control、Spring、Hibernate、Vert.x
- 如何安装jdk(如何安装jdk1.8)
- 标签列表
-
- mydisktest_v298 (34)
- document.appendchild (35)
- 头像打包下载 (61)
- acmecadconverter_8.52绿色版 (39)
- word文档批量处理大师破解版 (36)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- parsevideo (33)
- 个人网站源码 (37)
- centos7.4下载 (33)
- mysql 查询今天的数据 (34)
- intouch2014r2sp1永久授权 (36)
- 先锋影音源资2019 (35)
- jdk1.8.0_191下载 (33)
- axure9注册码 (33)
- pts/1 (33)
- spire.pdf 破解版 (35)
- shiro jwt (35)
- sklearn中文手册pdf (35)
- itextsharp使用手册 (33)
- 凯立德2012夏季版懒人包 (34)
- 反恐24小时电话铃声 (33)
- 冒险岛代码查询器 (34)
- 128*128png图片 (34)
- jdk1.8.0_131下载 (34)