show variables like 'log_bin';html
二進制日誌文件(binary log) 記錄了對mysql 數據庫執行更改的全部操做, 可是不包括 select 和 show 這類操做,python
由於這類操做對數據自己沒有修改. 然而 操做自己沒有對數據進行修改也可能會記錄二進制日誌,mysql
binlog文件位置能夠經過查詢獲得git
mysql> show variables like 'datadir'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | datadir | /var/lib/mysql/ | +---------------+-----------------+
其產生的binlog 文江將在 該目錄下存儲github
經過查詢可獲得當前mysql服務正在使用的 是哪一個文件spring
mysql> show master status\G *************************** 1. row *************************** File: binlog.000055 Position: 1065 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 1 row in set (0.00 sec)
咱們來看一下該路徑下都有啥sql
# pwd /var/lib/mysql # ls '#innodb_temp' binlog.000055 client-key.pem ib_logfile0 mysql.ibd server-cert.pem undo_002 auto.cnf binlog.index credit_txp ib_logfile1 nh_merchant server-key.pem vcc binlog.000051 bootdo demo ibdata1 performance_schema springbootv2 binlog.000052 ca-key.pem ec_common ibtmp1 private_key.pem sys binlog.000053 ca.pem foo internationalization public_key.pem test binlog.000054 client-cert.pem ib_buffer_pool mysql renren@002dfast undo_001 #
binlog文件通常是在mysql重啓時切換binlog文件, 也能夠手動切換當前使用的binlog文件,例如咱們當前使用的binlog文件是 "binlog.000055", 能夠經過 flush logs 手動切換 binlog 文件shell
mysql> flush logs; Query OK, 0 rows affected (0.01 sec) mysql> show master status\G *************************** 1. row *************************** File: binlog.000056 Position: 155 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 1 row in set (0.00 sec)
能夠看到mysql服務使用的binlog文件改成了 binlog.000056數據庫
看一下 binlog文件的配置參數緩存
說明:
max_binlog_size 指定二進制文件最大值, 若是超過該值將會產生一個新的二進制文件,後綴名+1, 默認大小爲1G
當使用innodb 存儲引擎時, 全部未提交的二進制日誌將會記錄到一個緩存中, 等事務提價時將從緩存中數據寫入二進制文件, 而該緩存的大小有 binlog_cache_size 指定, 默認大小爲32KB, 此外, 該緩衝是 基於session 的 , 即一個會話就會分配 一個指定大小的緩存, 所以不能設置過大, 可是當一個事務的記錄大於指定的緩存時, mysql會把緩存中的日誌寫入一個臨時文件中, 所以該值還不能設置太小. 能夠經過 show global status 查看binlog_cache_use(記錄使用緩衝寫入二進制文件次數) 、binlog_cache_disk_use(記錄使用臨時文件寫入二進制文件測試) 的狀態 來判斷是 binlog_cache_size 大小設置是否合適,
mysql> show variables like 'binlog_cache_size'; 大小默認32k +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | binlog_cache_size | 32768 | +-------------------+-------+ 1 row in set (0.01 sec) mysql> show global status like 'binlog_cache%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Binlog_cache_disk_use | 0 | 未使用過臨時文件寫入二進制文件 | Binlog_cache_use | 3 | +-----------------------+-------+ 2 rows in set (0.00 sec)
sync_binlog, 使用緩衝的來寫入二進制日誌的時候, sync_binlog=N 即表示寫到緩衝N次即同步磁盤
binlog-do-db 與 binlog-ignore-db 表示須要寫入或者忽略那些庫的日誌寫入二進制日誌文件,默認空
log-save-update 是當前mysql服務做爲slave 節點, 則當前節點是不會從master節點獲取二進制日誌文件在寫入本身的二進制文件的, 若是要開啓就是要在從節點再次寫入的話須要配置 log-save-update. 另外,若是mysql主從配置若是爲 master=>slave=>slave 的話,則該參數必須設置
binlog_format 二進制日誌文件格式
Row
日誌中會記錄成每一行數據被修改的形式,而後在 slave 端再對相同的數據進行修改。
優勢: 在 row 模式下,bin-log 中能夠不記錄執行的 SQL 語句的上下文相關的信息,僅僅只須要記錄那一條記錄被修改了,修改爲什麼樣了。因此 row 的日誌內容會很是清楚的記錄下每一行數據修改的細節,很是容易理解。並且不會出現某些特定狀況下的存儲過程或 function ,以及 trigger 的調用和觸發沒法被正確複製的問題。
缺點: 在 row 模式下,全部的執行的語句當記錄到日誌中的時候,都將以每行記錄的修改來記錄,這樣可能會產生大量的日誌內容,好比有這樣一條 update 語句:
UPDATE product SET owner_member_id = 'b' WHERE owner_member_id = 'a'
執行以後,日誌中記錄的不是這條 update 語句所對應的事件 (MySQL 以事件的形式來記錄 bin-log 日誌) ,而是這條語句所更新的每一條記錄的變化狀況,這樣就記錄成不少條記錄被更新的不少個事件。天然,bin-log 日誌的量就會很大。尤爲是當執行 alter table 之類的語句的時候,產生的日誌量是驚人的。由於 MySQL 對於 alter table 之類的表結構變動語句的處理方式是整個表的每一條記錄都須要變更,實際上就是重建了整個表。那麼該表的每一條記錄都會被記錄到日誌中。
Statement
每一條會修改數據的 SQL 都會記錄到 master 的 bin-log 中。slave 在複製的時候 SQL 進程會解析成和原來 master 端執行過的相同的 SQL 再次執行。
優勢: 在 statement 模式下,首先就是解決了 row 模式的缺點,不須要記錄每一行數據的變化,減小了 bin-log 日誌量,節省 I/O 以及存儲資源,提升性能。由於他只須要記錄在 master 上所執行的語句的細節,以及執行語句時候的上下文的信息。
缺點: 在 statement 模式下,因爲他是記錄的執行語句,因此,爲了讓這些語句在 slave 端也能正確執行,那麼他還必須記錄每條語句在執行的時候的一些相關信息,也就是上下文信息,以保證全部語句在 slave 端杯執行的時候可以獲得和在 master 端執行時候相同的結果。另外就是,因爲 MySQL 如今發展比較快,不少的新功能不斷的加入,使 MySQL 的複製遇到了不小的挑戰,天然複製的時候涉及到越複雜的內容,bug 也就越容易出現。在 statement 中,目前已經發現的就有很多狀況會形成 MySQL 的複製出現問題,主要是修改數據的時候使用了某些特定的函數或者功能的時候會出現,好比:sleep() 函數在有些版本中就不能被正確複製,在存儲過程當中使用了 last_insert_id() 函數,可能會使 slave 和 master 上獲得不一致的 id 等等。因爲 row 是基於每一行來記錄的變化,因此不會出現相似的問題。
Mixed
從 5.1.8 版本開始,MySQL 提供了除 Statement 和 Row 以外的第三種複製模式:Mixed,實際上就是前兩種模式的結合。
在 Mixed 模式下,MySQL 會根據執行的每一條具體的 SQL 語句來區分對待記錄的日誌形式,也就是在 statement 和 row 之間選擇一種。
新版本中的 statment 仍是和之前同樣,僅僅記錄執行的語句。而新版本的 MySQL 中對 row 模式也被作了優化,並非全部的修改都會以 row 模式來記錄,好比遇到表結構變動的時候就會以 statement 模式來記錄,若是 SQL 語句確實就是 update 或者 delete 等修改數據的語句,那麼仍是會記錄全部行的變動。
以上是對binlog 基本參數的配置作一個簡要說明
下面將經過開源binlog2sql 來嘗試恢復數據
binlog2sql 是大衆點評開源快速回滾的工具, 其原理如名字是經過mysql的binlog對數據進行恢復
binlog2sql 在github地址: https://github.com/danfengcao/binlog2sql
下面走一個案例來測試恢復數據
mysql版本5.7
python 版本 2.7.16
安裝
shell> git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql shell> pip install -r requirements.txt
注意: shell> git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql binlog2sql路徑下的requirements.txt文件須要調整 cat requirements.txt PyMySQL0.7.11 wheel0.29.0 mysql-replication==0.13 須要將PyMySQL由0.7.11調整爲0.9.3 shell> pip install -r requirements.txt
測試
準備數據庫 test, 表user, 確認當前使用的binlog文件是什麼
mysql> show master status\G; *************************** 1. row *************************** File: mysql-bin.000002 Position: 154 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 1 row in set (0.00 sec)
當前使用的binlog 文件是 mysql-bin.000002
手動向user 中插入幾條數據
INSERT INTO `test`.`user`(`id`, `name`) VALUES (1, '老趙'); INSERT INTO `test`.`user`(`id`, `name`) VALUES (2, '老高'); INSERT INTO `test`.`user`(`id`, `name`) VALUES (3, '你妹的');
此時數據庫中的數據是
![image-20210220153155284](/Users/jack/Library/Application Support/typora-user-images/image-20210220153155284.png)
此時咱們咔全刪了 DELETE TABLE user
;
使用binlog2sql 對案發時間的日誌作篩查
刪除數據
DELETE FROM `user`; # 案發時間大約在 2021-02-20 16:27:00 - 2021-02-20 16:29:00
使用binlog2sql 生成的回滾數據恢復數據
python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uroot -p'root' -dtest -tuser --start-file='mysql-bin.000002' --start-datetime='2021-02-20 16:27:00' --stop-datetime='2021-02-20 16:29:00' -B > rollback.sql | cat
結果:
jack@JackdeMacBook-Pro binlog2sql % python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uroot -p'root' -dtest -tuser --start-file='mysql-bin.000002' --start-datetime='2021-02-20 16:27:00' --stop-datetime='2021-02-20 16:29:00' -B > rollback.sql | cat INSERT INTO `test`.`user`(`id`, `name`) VALUES (3, '你妹的'); #start 4911 end 5175 time 2021-02-20 16:27:08 INSERT INTO `test`.`user`(`id`, `name`) VALUES (2, '老高'); #start 4911 end 5175 time 2021-02-20 16:27:08 INSERT INTO `test`.`user`(`id`, `name`) VALUES (1, '老趙'); #start 4911 end 5175 time 2021-02-20 16:27:08 jack@JackdeMacBook-Pro binlog2sql % jack@JackdeMacBook-Pro binlog2sql % cat rollback.sql INSERT INTO `test`.`user`(`id`, `name`) VALUES (3, '你妹的'); #start 4911 end 5175 time 2021-02-20 16:27:08 INSERT INTO `test`.`user`(`id`, `name`) VALUES (2, '老高'); #start 4911 end 5175 time 2021-02-20 16:27:08 INSERT INTO `test`.`user`(`id`, `name`) VALUES (1, '老趙'); #start 4911 end 5175 time 2021-02-20 16:27:08 jack@JackdeMacBook-Pro binlog2sql %
通過排查而後執行恢復數據 👌
參考
http://www.javashuo.com/article/p-kmrtlvvi-ve.html
http://www.javashuo.com/article/p-calivyfm-dt.html
MySQL技術內幕++InnoDB存儲引擎
記錄報錯