前言

“删库跑路”是程序员经常谈起的话题,今天,我就要教大家如何删!库!跑!路!

开个玩笑,今天文章的主题是如何使用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

模拟删除数据并恢复

  1. 首先新建数据库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. 插入两条数据,分别是 (1,‘A’),(2,‘B’)
INSERT INTO `table1` VALUES (1,'A'),(2,'B');
  1. 我们看一下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行。

  1. 然后,使用flush logs来主动刷新一次binlog
mysql> flush logs;
Query OK, 0 rows affectedmysql> 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则已经归档了,不会再写入新的日志进去了。

  1. 接下来我们在插入两条数据
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  |
+----+----+
  1. 这时候我们已经有了四条数据,我们再次flush logs,把mysql-bin.000002日志存档,开启新的mysql-bin.000003日志,这样,每次我们插入的数据彼此独立。实际情况下,binlog会比较复杂,这里也是做了简化,为了理解更方便。
mysql> flush logs;
Query OK, 0 rows affectedmysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      154 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set
  1. 然后我们删除id为4的数据(4,D),并且再次刷新binlog,如此一来,binlog.000003里面只有一条删除操作。
mysql> delete from table1 where id = 4;
Query OK, 1 row affectedmysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      423 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in setmysql> flush logs;
Query OK, 0 rows affectedmysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000004 |      154 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+
1 row in set
  1. 让我们来好好观察下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 setmysql> 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 */                                    |
  1. 我们的目的是恢复误删的数据,其实就是将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 入门实战系列
  • 爬虫相关技术文章
  • 逸闻趣事/好书分享/个人兴趣

个人公众号:后端技术漫谈

如果文章对你有帮助,不妨收藏,投币,转发,在看起来~

【删库跑路】使用Binlog日志恢复误删的MySQL数据相关推荐

  1. binlog日志_【删库跑路】使用Binlog日志恢复误删的MySQL数据

    前言 "删库跑路"是程序员经常谈起的话题,今天,我就要教大家如何删!库!跑!路! 开个玩笑,今天文章的主题是如何使用Mysql内置的Binlog日志对误删的数据进行恢复,读完本文, ...

  2. mysql data目录 清空_Mysql binlog备份数据及恢复数据,学会这个,我在也不怕删库跑路啦~...

    导读 我一直都主张,技多不压身(没有学不会的技术,只有不学习的人),多学一项技能,未来就少求人一次.网上经常听到xxx删库跑路,万一真的遇到了,相信通过今天的学习,也能将数据再恢复回来~~~ 介绍 记 ...

  3. 京东到家程序员删库跑路 ! 讲一讲 MySQL 数据备份杀手锏 binlog

    我们都知道,数据非常重要 网上也经常看到一些段子,某公司程序员对工作不满,删库跑路,老板损失惨重,欲哭无泪.这不最近又爆出一例,京东到家程序员离职当天删库跑路! 那么有没有什么解决方案? 即使数据库真 ...

  4. 从“删库跑路”聊聊开启BinLog防止误删表数据、结构及数据库

    最近网上很多同学都在疯传疫情删库=跑路,工作中误删数据或者数据库我们一定需要跑路吗?我看未必在 MySQL数据库中我们知道 binlog 日志记录了我们对数据库的所有操作接下来就来开启程序员自救之路 ...

  5. 『数据库』你以为删库跑路就能让你老板内(lei)牛(liu)满面--数据库的恢复技术

    数据库从入门到精通:戳我 文章目录 一. 事务的基本概念 1.事务 1.1what's the 事务: 1.2事务的定义 1.2.1 事务的显示定义 1.2.2 事务的隐式定义方式 2.事务的ACID ...

  6. 阿里十年DBA经验产品经理:真的不要再有一起删库跑路事件了

    最近网上又出一起删库跑路事件,本不想过多写此类事件文字,但从业13年,十年DBA工作经验,职业素养还是驱使自己写点内容,以期能够帮助广大企业客户. 本文主要以数据库产品从业者角度,介绍帮助企业减少意外 ...

  7. 同事删库跑路后,我连表名都不能修改了?

    事情是这样的,前几天隔壁部门的哥们在生产环境的数据库上,执行了一下drop命令,好嘛,活生生的删库跑路的例子居然真的在我身边发生了,好在运维同学给力,后来恢复了数据.事后听说这哥们虽然没被开除,但也吃 ...

  8. “删库跑路”重现江湖,技术和制度如何保障数据安全?

    摘要:近日,一则来自微盟官网的消息在业内引起轩然大波,"删库跑路"重现江湖--由此,关于如何从技术和制度两方面进行数据安全防范的关注和讨论广泛展开. 近日,一则来自微盟官网的消息在 ...

  9. 关闭删库跑路的后门,打造高可用的MySQL

    0 MySQL HA/Scalability 如何关上"删库跑路"的后门,维护我们的数据安全呢? 数据是当今Web,移动,社交,企业和云应用程序的流行货币.确保数据始终可用是任何组 ...

最新文章

  1. CRM_OPPORT_H_READ_OW used not so often
  2. 逆波兰计算器android源码简书,汪都能理解的逆波兰计算器(C++实现)
  3. stm32单片机OLED取字模软件使用 PCtoLCD2002
  4. 459.重复子字符串
  5. C++ Make、Makefile、CMake和CMakeLists关系
  6. 第一章课后习题源代码(笔记自用)
  7. 数模学习(七)--- 多元线性回归
  8. 关于Three.js 加载地面贴图的坑
  9. Postgis使用工具osm2pgsql导入OpenStreetMap数据
  10. 如何配置 Cilium 和 BGP 协同工作?
  11. gb 28181的20位编码简介
  12. 那些年你追过的女神:开发人员应该懂多少运维
  13. 大学计算机学习心得1000字,大学计算机学习心得体会
  14. stl如果开o2_打开STL文件的四种最佳方法
  15. 【年终总结】——梦想起航
  16. 拓嘉辰丰电商:拼多多店铺指标有哪些对于参加活动比较重要
  17. Ant 中的一些命令
  18. ES学习看这一篇文章就够了
  19. SCAU18709魔法
  20. 新买的电脑如何用U盘装系统

热门文章

  1. 2012年第一批中关村高端领军人才公示公告
  2. Spring Boot入门(24):Spring Boot事务 | 超级详细,建议收藏
  3. 大数据-玩转数据-IDEA创建Maven工程
  4. HALEY KOEHN--a good graphic designer recent years
  5. 为Latex生成的PDF设置背景色
  6. MLY -- 13.Error analysis:look at dev set examples to evaluate ideas
  7. unity游戏开发知识检测
  8. 2D草图实时3D建模,可跑可跳无需手动绑定骨骼丨开源
  9. 学计算机主修,大学计算机专业自我介绍(精选5篇)
  10. SDK ..\OBJ\XXX.axf: Error: L6218E: Undefined symbol XXXX (referred from XXX.o).