前奏
DBA/開發 工做過程當中誤刪數據、誤改數據是常有的事,做爲 DBA 如何快速填坑呢python
(1)利用最近的全量備份+增量binlog備份,恢復到誤操做以前的狀態,可是隨着數據量的增大,binlog的增多,恢復起來很費時。mysql
(2)若是binlog的格式爲row,那麼就能夠將binlog解析出來生成反向的原始SQLgit
固然還有其餘的一些操做方法,這裏暫不展開來說,咱們今天主要介紹binlog2sql github
大衆點評開源的一個 MySQL 閃回工具 -- binlog2sqlsql
閃回原理
binlog 概述:bash
MySQL binlog 以event 的形式,記錄了 MySQL server 從啓用 binlog 以來的全部變動信息,可以幫實現這之間的全部變化。工具
MySQL 引用 binglog 的主要目的:1、主從複製;2、某些備份還原操做須要從新應用 binlogspa
既然 binlog 以 event 形式記錄了全部的變動信息,那麼咱們把須要回滾的event,從後往前回滾回去便可。code
閃回前提:log_bin 爲 ON;binlog_row_image 爲full;binlog_format 爲 row;orm
| log_bin | ON | | binlog_row_image | full | | binlog_format | ROW |
回滾操做:
- 對於 delete 操做,咱們從 binlog 提取出 delete 信息,反向生成 insert 回滾語句;
- 對於 insert 操做,反向生成 delete 回滾語句;
- 對於update操做,回滾sql應該交換SET和WHERE的值。
閃回實戰
(一) 安裝binlog2sql
(root@localhost) [employees]> select * from titles where emp_no <= 10007 ; +--------+-----------------+------------+---------+ | emp_no | title | from_date | to_date | +--------+-----------------+------------+---------+ | 10001 | Senior | 1986-06-26 | NULL | | 10002 | Staff | 1996-08-03 | NULL | | 10003 | Senior Engineer | 1995-12-03 | NULL | | 10004 | Engineer | 1986-12-01 | NULL | | 10004 | Senior Engineer | 1995-12-01 | NULL | | 10005 | Senior Staff | 1996-09-12 | NULL | | 10005 | Staff | 1989-09-12 | NULL | | 10006 | Senior Engineer | 1990-08-05 | NULL | | 10007 | Senior Staff | 1996-02-11 | NULL | | 10007 | Staff | 1989-02-10 | NULL | +--------+-----------------+------------+---------+ 10 rows in set (0.00 sec) (root@localhost) [employees]> delete from titles where emp_no <= 10007 ; Query OK, 10 rows affected (0.00 sec) (root@localhost) [employees]> select * from titles where emp_no <= 10007 ; Empty set (0.00 sec)
(root@localhost) [employees]> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000015
Position: 364596
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)
(root@localhost) [employees]> select now();
+---------------------+
| now() |
+---------------------+
| 2019-01-23 16:26:43 |
+---------------------+
1 row in set (0.00 sec)
[root@05 binlog2sql]# python binlog2sql.py -h127.0.0.1 -P3306 -uroot -p123 -demployees -t titles --start-file=mysql-bin.000015 --start-datetime='2019-01-23 16:20:04'
[root@05 binlog2sql]# python binlog2sql.py --flashback -h127.0.0.1 -P3306 -uroot -p123 -demployees -t titles --start-file=mysql-bin.000015 --start-datetime='2019-01-23 16:20:04' >tit.sql
[root@05 binlog2sql]# mysql -uroot -p123 --database employees < tit.sql
[root@05 binlog2sql]# python binlog2sql.py -h127.0.0.1 -P3306 -uroot -p123 -demployees -t titles --start-file=mysql-bin.000015 --start-datetime='2019-01-23 16:20:04'
DELETE FROM `employees`.`titles` WHERE `emp_no`=10001 AND `to_date` IS NULL AND `from_date`='1986-06-26' AND `title`='Senior' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10002 AND `to_date` IS NULL AND `from_date`='1996-08-03' AND `title`='Staff' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10003 AND `to_date` IS NULL AND `from_date`='1995-12-03' AND `title`='Senior Engineer' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10004 AND `to_date` IS NULL AND `from_date`='1986-12-01' AND `title`='Engineer' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10004 AND `to_date` IS NULL AND `from_date`='1995-12-01' AND `title`='Senior Engineer' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10005 AND `to_date` IS NULL AND `from_date`='1996-09-12' AND `title`='Senior Staff' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10005 AND `to_date` IS NULL AND `from_date`='1989-09-12' AND `title`='Staff' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10006 AND `to_date` IS NULL AND `from_date`='1990-08-05' AND `title`='Senior Engineer' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10007 AND `to_date` IS NULL AND `from_date`='1996-02-11' AND `title`='Senior Staff' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
DELETE FROM `employees`.`titles` WHERE `emp_no`=10007 AND `to_date` IS NULL AND `from_date`='1989-02-10' AND `title`='Staff' LIMIT 1; #start 364141 end 364565 time 2019-01-23 16:23:59
You have mail in /var/mail/root
[root@05 binlog2sql]# python binlog2sql.py --flashback -h127.0.0.1 -P3306 -uroot -p123 -demployees -t titles --start-file=mysql-bin.000015 --start-datetime='2019-01-23 16:20:04' >tit.sql
[root@05 binlog2sql]# mysql -uroot -p123 --database employees < tit.sql
(root@localhost) [employees]> select * from titles where emp_no <= 10007 ;
+--------+-----------------+------------+---------+
| emp_no | title | from_date | to_date |
+--------+-----------------+------------+---------+
| 10001 | Senior | 1986-06-26 | NULL |
| 10002 | Staff | 1996-08-03 | NULL |
| 10003 | Senior Engineer | 1995-12-03 | NULL |
| 10004 | Engineer | 1986-12-01 | NULL |
| 10004 | Senior Engineer | 1995-12-01 | NULL |
| 10005 | Senior Staff | 1996-09-12 | NULL |
| 10005 | Staff | 1989-09-12 | NULL |
| 10006 | Senior Engineer | 1990-08-05 | NULL |
| 10007 | Senior Staff | 1996-02-11 | NULL |
| 10007 | Staff | 1989-02-10 | NULL |
+--------+-----------------+------------+---------+
10 rows in set (0.01 sec
TIPS
- 閃回的目標:快速篩選出真正須要回滾的數據。
- 先根據庫、表、時間作一次過濾,再根據位置作更準確的過濾。
- 因爲數據一直在寫入,要確保回滾sql中不包含其餘數據。可根據是不是同一事務、誤操做行數、字段值的特徵等等來幫助判斷。
- 執行回滾sql時若有報錯,須要查實具體緣由,通常是由於對應的數據已發生變化。因爲是嚴格的行模式,只要有惟一鍵(包括主鍵)存在,就只會報某條數據不存在的錯,沒必要擔憂會更新不應操做的數據。業務若是有特殊邏輯,數據回滾可能會帶來影響。
- 若是隻回滾某張表,而且該表有關聯表,關聯表並不會被回滾,需與業務方溝通清楚
哪些數據須要回滾,讓業務方來判斷!
MySQL binlog2sql的更多內容可參考:
https://github.com/danfengcao/binlog2sql/blob/master/example/mysql-flashback-priciple-and-practice.md