“删库跑路”是程序员常常谈起的话题,今天,我就要教你们如何删!库!跑!路!html
开个玩笑,今天文章的主题是如何使用Mysql内置的Binlog日志对误删的数据进行恢复,读完本文,你可以了解到:mysql
写这篇文章的初衷,是有一次我真的险些把测试数据库的一张表给删除了,当时吓出一身冷汗。缘由是因为Spring JPA的配置中,有一个spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop,其用途是每次加载hibernate时根据model类生成表,可是sessionFactory一关闭,表就自动删除。,这个可不能随便配置上去,直接就把你原来存在的表给drop了!程序员
好了,回归正题,这篇文章就是想让你们放心,MySQL就算进行了误删操做,也基本都可以抢救回来。尤为是大公司内,数据可不是你想删就能删掉的,有无数权限/备份阻拦着你。面试
binlog是记录全部数据库表结构变动(例如CREATE、ALTER TABLE…)以及表数据修改(INSERT、UPDATE、DELETE…)的二进制日志。
binlog不会记录SELECT和SHOW这类操做,由于这类操做对数据自己并无修改,但你能够经过查询通用日志来查看MySQL执行过的全部语句。算法
看了上面binlog的定义,你们也应该能大体推理出binlog的三大用途:spring
因此说,想要可以恢复数据,首先,你得打开Mysql的binlog,在日常你本身安装的单机Mysql中,默认状况下不会开启。下面就一步步地实践下如何开启你服务器上的Binlog日志。sql
首先进入数据库控制台,运行指令:数据库
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配置文件,执行命令:ubuntu
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
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;
INSERT INTO `table1` VALUES (1,'A'),(2,'B');
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行。
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 | +----+----+
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
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> 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 */ |
在本文中,咱们只作一个恢复两条插入语句的操做,执行语句:
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
http://www.javashuo.com/article/p-mtfmpaby-du.html
https://www.jianshu.com/p/564fcc2b5e31
http://www.javashuo.com/article/p-sbrfltcr-z.html
我是一名后端开发工程师。
主要关注后端开发,数据安全,爬虫,物联网,边缘计算等方向,欢迎交流。
公众号:后端技术漫谈.jpg若是文章对你有帮助,不妨收藏,投币,转发,在看起来~