解到:

  • 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

首先进入数据库控制台,运行指令:

  1. mysql> show variables likelog_bin%‘;
  2. +---------------------------------+-------+
  3. | Variable_name | Value |
  4. +---------------------------------+-------+
  5. | log_bin | OFF |
  6. | log_bin_basename | |
  7. | log_bin_index | |
  8. | log_bin_trust_function_creators | OFF |
  9. | log_bin_use_v1_row_events | OFF |
  10. +---------------------------------+-------+
  11. 5 rows in set (0.00 sec)

可以看到我们的binlog是关闭的,都是OFF。接下来我们需要修改Mysql配置文件,执行命令:

  1. sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

在文件末尾添加:

  1. log-bin=/var/lib/mysql/mysql-bin

保存文件,重启mysql服务:

  1. sudo service mysql restart

重启完成后,查看下mysql的状态:

  1. systemctl status mysql.service

这时,如果你的mysql版本在5.7或更高版本,就会报错:

  1. 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)
  2. 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)
  3. 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
  4. 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 ...
  5. 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 havent provided the mandatory server-id. Please refer to the proper server
  6. Jan 06 15:49:58 VM-0-11-ubuntu mysqld[5930]: 2020-01-06T07:49:58.363747Z 0 [ERROR] Aborting
  7. Jan 06 15:49:58 VM-0-11-ubuntu mysqld[5930]: 2020-01-06T07:49:58.363922Z 0 [Note] Binlog end
  8. Jan 06 15:49:58 VM-0-11-ubuntu mysqld[5930]: 2020-01-06T07:49:58.364108Z 0 [Note] /usr/sbin/mysqld: Shutdown complete
  9. 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随机给个数字就可以。

  1. server-id=123454

模拟删除数据并恢复

  1. 首先新建数据库mytest,新建一张表table1,结构见下方SQL代码
  1. CREATE DATABASE `test` ;
  2. USE `test`;
  3. DROP TABLE IF EXISTS `table1`;
  4. CREATE TABLE `table2` (
  5. `id` int(11) DEFAULT NULL,
  6. `name` varchar(20) DEFAULT NULL
  7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 插入两条数据,分别是 (1,‘A‘),(2,‘B‘)
  1. INSERT INTO `table1` VALUES (1,‘A‘),(2,‘B‘);
  1. 我们看一下binlog日志的状态,使用show master status
  1. mysql> show master status
  2. -> ;
  3. +------------------+----------+--------------+------------------+-------------------+
  4. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
  5. +------------------+----------+--------------+------------------+-------------------+
  6. | mysql-bin.000001 | 690 | | | |
  7. +------------------+----------+--------------+------------------+-------------------+
  8. 1 row in set

binlog日志特征:每当我们重启MySQL一次,会自动生成一个binlog文件,当然,我们也可以手动的来刷新binlog文件,通过 flush logs,同样会新创建一个binlog文件。实际上当服务器在重启时,也会调用flush logs操作。

上图代码中可以看到,现在我们正在使用 mysql-bin.0000001 ,并且这个文件现在正在记录到690行。

  1. 然后,使用flush logs来主动刷新一次binlog
  1. mysql> flush logs;
  2. Query OK, 0 rows affected
  3. mysql> show master status
  4. -> ;
  5. +------------------+----------+--------------+------------------+-------------------+
  6. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
  7. +------------------+----------+--------------+------------------+-------------------+
  8. | mysql-bin.000002 | 154 | | | |
  9. +------------------+----------+--------------+------------------+-------------------+
  10. 1 row in set

可以看到,现在日志文件在 mysql-bin.000002 文件中,位置为154。也就是我们主动刷新了一次binlog,生成了新的000002,而000001则已经归档了,不会再写入新的日志进去了。

  1. 接下来我们在插入两条数据
  1. insert into table1 values (3,‘C‘);
  2. insert into table1 values (4,‘D‘);
  3. mysql> select * from table1;
  4. +----+----+
  5. | id |name|
  6. +----+----+
  7. | 1 | A |
  8. | 2 | B |
  9. | 3 | C |
  10. | 4 | D |
  11. +----+----+
  1. 这时候我们已经有了四条数据,我们再次flush logs,把mysql-bin.000002日志存档,开启新的mysql-bin.000003日志,这样,每次我们插入的数据彼此独立。实际情况下,binlog会比较复杂,这里也是做了简化,为了理解更方便。
  1. mysql> flush logs;
  2. Query OK, 0 rows affected
  3. mysql> show master status;
  4. +------------------+----------+--------------+------------------+-------------------+
  5. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
  6. +------------------+----------+--------------+------------------+-------------------+
  7. | mysql-bin.000003 | 154 | | | |
  8. +------------------+----------+--------------+------------------+-------------------+
  9. 1 row in set
  1. 然后我们删除id为4的数据(4,D),并且再次刷新binlog,如此一来,binlog.000003里面只有一条删除操作。
  1. mysql> delete from table1 where id = 4;
  2. Query OK, 1 row affected
  3. mysql> show master status;
  4. +------------------+----------+--------------+------------------+-------------------+
  5. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
  6. +------------------+----------+--------------+------------------+-------------------+
  7. | mysql-bin.000003 | 423 | | | |
  8. +------------------+----------+--------------+------------------+-------------------+
  9. 1 row in set
  10. mysql> flush logs;
  11. Query OK, 0 rows affected
  12. mysql> show master status;
  13. +------------------+----------+--------------+------------------+-------------------+
  14. | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
  15. +------------------+----------+--------------+------------------+-------------------+
  16. | mysql-bin.000004 | 154 | | | |
  17. +------------------+----------+--------------+------------------+-------------------+
  18. 1 row in set
  1. 让我们来好好观察下mysql-bin.00002和mysql-bin00003两个binlog,使用命令:show binlog events in ‘mysql-bin.000003‘
  1. mysql> show binlog events in mysql-bin.000003‘;
  2. +------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
  3. | Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
  4. +------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
  5. | mysql-bin.000003 | 4 | Format_desc | 123456 | 123 | Server ver: 5.7.28-0ubuntu0.16.04.2-log, Binlog ver: 4 |
  6. | mysql-bin.000003 | 123 | Previous_gtids | 123456 | 154 | |
  7. | mysql-bin.000003 | 154 | Anonymous_Gtid | 123456 | 219 | SET @@SESSION.GTID_NEXT= ANONYMOUS |
  8. | mysql-bin.000003 | 219 | Query | 123456 | 293 | BEGIN |
  9. | mysql-bin.000003 | 293 | Table_map | 123456 | 343 | table_id: 108 (test.table1) |
  10. | mysql-bin.000003 | 343 | Delete_rows | 123456 | 392 | table_id: 108 flags: STMT_END_F |
  11. | mysql-bin.000003 | 392 | Xid | 123456 | 423 | COMMIT /* xid=39 */ |
  12. +------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
  13. 7 rows in set
  14. mysql> show binlog events in mysql-bin.000002‘;
  15. +------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
  16. | Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
  17. +------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
  18. | mysql-bin.000002 | 4 | Format_desc | 123456 | 123 | Server ver: 5.7.28-0ubuntu0.16.04.2-log, Binlog ver: 4 |
  19. | mysql-bin.000002 | 123 | Previous_gtids | 123456 | 154 | |
  20. | mysql-bin.000002 | 154 | Anonymous_Gtid | 123456 | 219 | SET @@SESSION.GTID_NEXT= ANONYMOUS |
  21. | mysql-bin.000002 | 219 | Query | 123456 | 293 | BEGIN |
  22. | mysql-bin.000002 | 293 | Table_map | 123456 | 343 | table_id: 108 (test.table1) |
  23. | mysql-bin.000002 | 343 | Write_rows | 123456 | 390 | table_id: 108 flags: STMT_END_F |
  24. | mysql-bin.000002 | 390 | Xid | 123456 | 421 | COMMIT /* xid=34 */ |
  25. | mysql-bin.000002 | 421 | Anonymous_Gtid | 123456 | 486 | SET @@SESSION.GTID_NEXT= ANONYMOUS |
  26. | mysql-bin.000002 | 486 | Query | 123456 | 560 | BEGIN |
  27. | mysql-bin.000002 | 560 | Table_map | 123456 | 610 | table_id: 108 (test.table1) |
  28. | mysql-bin.000002 | 610 | Write_rows | 123456 | 659 | table_id: 108 flags: STMT_END_F |
  29. | mysql-bin.000002 | 659 | Xid | 123456 | 690 | COMMIT /* xid=35 */ |
  30. | mysql-bin.000002 | 690 | Rotate | 123456 | 737 | mysql-bin.000003;pos=4 |
  31. +------------------+-----+----------------+-----------+-------------+--------------------------------------------------------+
  32. 13 rows in set

虽然有很多看似复杂的指令,但是还是不难看出,在02里,有两条写操作,03里有一条删除操作。

一条插入操作的完整日志是这样:

  1. | mysql-bin.000002 | 154 | Anonymous_Gtid | 123456 | 219 | SET @@SESSION.GTID_NEXT= ANONYMOUS |
  2. | mysql-bin.000002 | 219 | Query | 123456 | 293 | BEGIN |
  3. | mysql-bin.000002 | 293 | Table_map | 123456 | 343 | table_id: 108 (test.table1) |
  4. | mysql-bin.000002 | 343 | Write_rows | 123456 | 390 | table_id: 108 flags: STMT_END_F |
  5. | mysql-bin.000002 | 390 | Xid | 123456 | 421 | COMMIT /* xid=34 */ |
  1. 我们的目的是恢复误删的数据,其实就是将binlog.000002日志的两条插入记录重演一遍,而不需要取理会binlog.000003的操作(因为删除是一个误操作)
    所以现在能理解为什么我们频繁刷新binlog了吧,当然,在实际的线上环境中,我们肯定需要将binlog导出后,仔细筛选出误操作,并将其排除,之后再运行binlog。

在本文中,我们只做一个恢复两条插入语句的操作,执行语句:

  1. 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所在路径。

参数描述:

  1. --start-datetime:从二进制日志中读取指定等于时间戳或者晚于本地计算机的时间
  2. --stop-datetime:从二进制日志中读取指定小于时间戳或者等于本地计算机的时间 取值和上述一样
  3. --start-position:从二进制日志中读取指定position 事件位置作为开始。
  4. --stop-position:从二进制日志中读取指定position 事件位置作为事件截至

执行成功后,再次查看表table1,可以看到两条新的id=3和4的数据被插入了进来。恢复成功了。

  1. mysql> select * from table1;
  2. +----+----+
  3. | id |name|
  4. +----+----+
  5. | 1 | A |
  6. | 2 | B |
  7. | 3 | C |
  8. | 3 | C |
  9. | 4 | D |
  10. +----+----+
  11. 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

使用Binlog日志恢复误删的MySQL数据的更多相关文章

  1. MySQL通过binlog日志恢复数据

    一.查看下自己的MySQL是否开启了binlog日志 # 是否启用binlog日志 OFF:关闭 ON:开启 show variables like 'log_bin'; 二.开启binlog日志 在 ...

  2. 解说mysql之binlog日志以及利用binlog日志恢复数据

    众所周知,binlog日志对于mysql数据库来说是十分重要的.在数据丢失的紧急情况下,我们往往会想到用binlog日志功能进行数据恢复(定时全备份+binlog日志恢复增量数据部分),化险为夷! 废 ...

  3. Mysql之binlog日志说明及利用binlog日志恢复数据操作记录

    众所周知,binlog日志对于mysql数据库来说是十分重要的.在数据丢失的紧急情况下,我们往往会想到用binlog日志功能进行数据恢复(定时全备份+binlog日志恢复增量数据部分),化险为夷! 一 ...

  4. Mysql利用binlog日志恢复数据操作(转)

    a.开启binlog日志:1)编辑打开mysql配置文件/etc/mys.cnf[root@vm-002 ~]# vim /etc/my.cnf在[mysqld] 区块添加 log-bin=mysql ...

  5. 【转】Mysql之binlog日志说明及利用binlog日志恢复数据操作记录

    众所周知,binlog日志对于mysql数据库来说是十分重要的.在数据丢失的紧急情况下,我们往往会想到用binlog日志功能进行数据恢复(定时全备份+binlog日志恢复增量数据部分),化险为夷! 废 ...

  6. MySQL二进制binlog日志说明以及利用binlog日志恢复数据

    MySQL的binlog日志对于mysql数据库来说是十分重要的.在数据丢失的紧急情况下,我们往往会想到用binlog日志功能进行数据恢复(定时全量备份+binlog日志恢复增量数据部分). 一.关于 ...

  7. MySQL的binlog日志恢复(转)

    binlog 基本认识 MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日 ...

  8. 使用全备+binlog日志恢复数据库

    1.binlog日志类型 Statement 只记录执行的sql语句,磁盘占用少,但是恢复的时候容易出问题.InodeDB不能使用Statement . Row 记录修改后的具体数据,磁盘占用较多 M ...

  9. MySQL日志恢复误删记录

    1.查询日志是否开启 show variables like"log_"; 2.查询是用的哪个日志文件 show master status; 3.定位是在什么时间误删的 /usr ...

随机推荐

  1. linux系统中离线安装python3.7过程记录

    最近公司新弄来一台linux  redhat 4.4.7服务器,准备在上面离线安装python3.7,安装过程中出现一些问题,特此记录下来. 首先在python官网上下载了 Python-3.7.3. ...

  2. vue 使用中的小技巧 (一)

    在vue的使用过程中会遇到各种场景,当普通使用时觉得没什么,但是或许优化一下可以更高效更优美的进行开发.下面有一些我在日常开发的时候用到的小技巧 data 和 Object.freeze 每个Vue实 ...

  3. Ceph对象主本损坏的修复方法

    前言 问题的触发是在进行一个目录的查询的时候,osd就会挂掉,开始以为是osd操作超时了,后来发现每次访问这个对象都有问题 log [WRN] : slow request 60.793196 sec ...

  4. C# 9.0新特性详解系列之二:扩展方法GetEnumerator支持foreach循环

    1.介绍 我们知道,我们要使一个类型支持foreach循环,就需要这个类型满足下面条件之一: 该类型实例如果实现了下列接口中的其中之一: System.Collections.IEnumerable ...

  5. xdebug不显示

  6. PVE简单迁移虚拟机

    工作中有2台PVE节点,但是没有做集群,如果有集群可以很方便的进行迁移.本次迁移的目的是: 目前有一台PVE1节点装的虚机资源使用较多,想迁移某台虚机到另一台PVE2. 1 备份 备份在web页面操作 ...

  7. 【PUPPETEER】初探之执行JavaScript方法(六)

    一.知识点 page.evaluate() document.querySelector().value = ''; 二.解析知识点 page.evaluate(),查看puppeteer 的api ...

  8. ELK---- Elasticsearch 使用ik中文分词器增加拓展热词

    进入到我们ik分词器安装目录下的config目录 cd /usr/local/myapp/elasticsearch-6.4.3/plugins/ik/configvi IKAnalyzer.cfg. ...

  9. Codeforces Round #488 by NEAR (Div. 2)

    A 开个桶记录是否出现即可. 时间复杂度 \(O\left(n+m\right)\). B 按能力值从小到大依次加入,然后维护前 \(k\) 大的金币数即可. 时间复杂度 \(O\left(n\log ...

  10. linux ssh远程连接控制 linux(centOS) 口令、密钥连接

    sshd服务提供两种安全验证的方法: 基于口令的安全验证:经过验证帐号与密码即可登陆到远程主机. 基于密钥的安全验证:需要在本地生成"密钥对"后将公钥传送至服务端,进行公共密钥的比 ...