「刪庫跑路」是程序員常常談起的話題,今天,我就要教你們如何刪!庫!跑!路!html
開個玩笑,今天文章的主題是如何使用Mysql內置的Binlog日誌對誤刪的數據進行恢復,讀完本文,你可以瞭解到:mysql
寫這篇文章的初衷,是有一次我真的險些把測試數據庫的一張表給刪除了,當時嚇出一身冷汗。緣由是因爲Spring JPA的配置中,有一個spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop
,其用途是每次加載hibernate時根據model類生成表,可是sessionFactory一關閉,表就自動刪除。
,這個可不能隨便配置上去,直接就把你原來存在的表給drop了!git
好了,迴歸正題,這篇文章就是想讓你們放心,MySQL就算進行了誤刪操做,也基本都可以搶救回來。尤爲是大公司內,數據可不是你想刪就能刪掉的,有無數權限/備份阻攔着你。程序員
binlog是記錄全部數據庫表結構變動(例如CREATE、ALTER TABLE…)以及表數據修改(INSERT、UPDATE、DELETE…)的二進制日誌。 binlog不會記錄SELECT和SHOW這類操做,由於這類操做對數據自己並無修改,但你能夠經過查詢通用日誌來查看MySQL執行過的全部語句。github
看了上面binlog的定義,你們也應該能大體推理出binlog的三大用途:面試
因此說,想要可以恢復數據,首先,你得打開Mysql的binlog,在日常你本身安裝的單機Mysql中,默認狀況下不會開啓。下面就一步步地實踐下如何開啓你服務器上的Binlog日誌。算法
首先進入數據庫控制檯,運行指令:spring
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配置文件,執行命令:sql
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');
複製代碼
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
來主動刷新一次binlogmysql> 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
複製代碼
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
複製代碼
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了吧,固然,在實際的線上環境中,咱們確定須要將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數據單獨拿出來還原,作到清晰可辨,也加快恢復效率。
我是一名後端開發工程師。
主要關注後端開發,數據安全,爬蟲,物聯網,邊緣計算等方向,歡迎交流。
若是文章對你有幫助,不妨收藏,投幣,轉發,在看起來~