關注微信公眾號【程序員白澤】,進入白澤的知識分享星球🌍
前言
作為《手撕MySQL》系列的第二篇文章,今天介紹一下MySQL的二進制日志(bin log),注意不要和MySQL的InnoDB存儲引擎特有的重寫日志(redo log)混淆,bin log是記錄所有數據庫表數據及表結構變更的二進制日志(不會記錄查詢操作),借助這個日志可以實現:數據恢復
和主從復制
(不難理解,因為所有涉及變更的操作都記錄了下來,可以追溯)。
這篇文章側重於講解使用bin log進行數據恢復,下一篇文章講解主從復制。
預備知識
SSH工具推薦
接下來會頻繁在控制台終端中輸入命令,因此推薦一款開源免費的ssh客戶端electerm:https://github.com/electerm/electerm,這是地址,可以直接下載安裝,非常好用!
bin log 狀態管理
在開始講解bin log可以提供的兩個功能之前,先要學會管理自己MySQL服務的bin log狀態,並且通過修改參數對其進行控制。先來查看一下自己MySQL服務是否已經開啟了bin log,可以看到我的二進制日志已經開啟。如果你的沒有,這里建議通過修改MySQL配置文件的方式將bin log聲明為開啟,然后重新啟動MySQL服務即可。
以Linux系統為例,MySQL數據庫是按照 /etc/my.cnf
—— /etc/mysql/my.cnf
—— /usr/local/mysql/etc/my.cnf
—— ~/.my.cnf
的順序讀取配置文件的,且如果出現參數重復設置則后一個配置文件中參數會覆蓋前者。如果你的MySQL服務沒有配置文件,那就直接自己創建一個,放在上面某個位置之一,然后在創建的配置文件中輸入你從網上搜到的設置bin log開啟的配置代碼,重啟MySQL服務即可。
現在假設你已經開啟了MySQL的二進制日志,如下:
mysql> show variables like '%log_bin%';
bin log 數據文件
觀察上面的查詢結果,可以看到兩個路徑變量:log_bin_basename
和log_bin_index
,分別表示bin log開啟后,數據文件的生成位置(/usr/local/mysql/data/
)和文件名規則(binlog.xxxxx1、binlog.xxxxx2以此類推),以及索引文件binlog.index
,其中存放着bin log數據文件列表。
# 查看MySQL數據文件列表(大部分MySQL數據文件都在這個路徑下,下面展示部分,主要是bin log相關的數據文件和索引文件)
lilithgamesdeMacBook-Pro-42:~ lilithgames$ sudo ls -al /usr/local/mysql/data
# 查看bin log索引文件內容(完全對應上面列出的三個數據文件)
lilithgamesdeMacBook-Pro-42:~ lilithgames$ sudo cat /usr/local/mysql/data/binlog.index
既然上面說到,bin log記錄所有對數據庫的更改操作,那么它是將SQL語句記錄在數據文件中還是將改動之后的行結果記錄下來呢?這里有三種記錄模式:
- ROW:數據文件中會記錄每一行數據被修改的情況,這樣就能保證在恢復數據或者主從復制時不會因為一些函數(如now()函數執行兩次獲取的時間是不一致的)導致數據不完全一致的情況,缺點是對於整張表的修改會導致大量數據插入到數據文件中。
- SRATEMENT:記錄修改數據的SQL語句,和ROW相反,在數據同步時,某些情況下會出現不完全一致的情況。
- MIXED:混合使用上面兩種記錄模式,在一般情況下使用SRATEMENT,在特殊情況使用ROW。
但是,新版本MySQL的ROW模式已經進行了優化,對於表結構的修改會以STATEMENT模式記錄,而對於記錄的修改則會在數據文件中記錄所有的行的變更。因此,ROW模式是bin log默認的工作模式。
mysql> show variables like 'binlog_format';
數據恢復
准備數據
Talk is cheap,show me the code!我知道你已經迫不及待想體驗bin log的數據恢復了,那就讓我們開始吧~
為了方便展示,我們在MySQL登錄態下執行flush logs
命令,可以生成一個新的日志文件(為了將后面操作數據庫的命令單獨放在一個新的數據文件中方便查看)
# 生成新的二進制數據文件(序號增加)
mysql> flush logs;
# 查看當前所有二進制數據文件
mysql> show binary logs;
因為我執行了兩次flush log命令,因此以此生成了兩個新的二進制數據文件,而且明顯可以看到這兩個數據文件很小,因為還沒有新的修改表的操作被記錄下來。
接下來我們建立一個測試數據庫:test_database
,然后創建一張用戶表,並且為其插入幾條測試數據。
# 創建user表
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)) ENGINE = INNODB DEFAULT CHARSET=utf8;
# 插入測試用戶數據
INSERT INTO user (id, username) VALUES (null, 'AAA');
INSERT INTO user (id, username) VALUES (null, 'BBB');
INSERT INTO user (id, username) VALUES (null, 'CCC');
# 查詢測試
SELECT * FROM user
接下來我們通過mysqlbinlog
命令來查看bin log的數據文件(猜測表的變更被記錄在binlog.000010二進制文件中),這里展示一部分。
sudo mysqlbinlog /usr/local/mysql/data/binlog.000010
下面是binlog.000010文件的部分內容,我們找到了建user表
的語句(上面說了,新版MySQL的ROW數據記錄模式對於表結構的更改是STATEMENT形式的),下面是數據文件中一些重要的字段解釋:
- 第一個at表示一個事件的起始位置pos,中間的是此次事件的二進制數據,而末尾的at表示下一個事件開始位置pos(也就是當前時間的結束位置pos)
- 220303 17:59:37 server id 1 表示server 1執行該事件的時間
- exec_time 表示執行時間(具體時間在主從復制時master和slave有所不同,下篇文章講解)
# 這個mysqlbinlog命令還可以添加參數,如指定查詢開始pos到結束pos之間的數據,Google一下~
sudo mysqlbinlog /usr/local/mysql/data/binlog.000010
# 當然,不借助mysqlbinlog命令,在mysql登錄狀態下也是可以直接查詢bin log數據文件內容的,測試如下:
mysql> show binlog events in 'binlog.000010' from 447 limit 10;
模擬失誤
# 失誤刪除id為1的用戶
DELETE FROM user where id=1
# 又插入兩個用戶
INSERT INTO user (id, username) VALUES (null, 'DDD');
INSERT INTO user (id, username) VALUES (null, 'EEE');
數據恢復
要明確的是:借助bin log二進制日志文件進行數據恢復的本質,是重新執行兩個pos區間內的SQL(所以上面才花了較大篇幅講解查看二進制文件,為的是學會定位pos點,也就是at后面那個數字)
首先通過mysql命令查看binlog.000010數據文件
mysql> show binlog events in 'binlog.000010';
這里要找到兩個pos點,一個是user表建立的pos:447,另一個是執行delete操作之前的pos,這里要求結束的pos不能直接選擇delete_rows操作的pos:1864,而是要選擇它前一個commit事件的下一個pos:1636(否則會出現警告⚠️)
使用mysqlbinlog命令生成pos為447—1636之間的SQL文件(上面說了數據恢復的本質是重新執行兩個pos區間內的SQL語句),通過下面的命令,生成了一個return.sql文件。
sudo mysqlbinlog --start-position=447 --stop-position=1636 /usr/local/mysql/data/binlog.000010 > return.sql
執行return.sql文件(相當於又執行了一遍這個區間的SQL語句),進行數據恢復!大功告成!刪除的AAA回來了!!不用被開除了!!
mysql> source ~/return.sql
結束語
本篇文章簡單講述了利用bin log進行數據恢復的案例,並且花費了較大篇幅講解一些bin log的基礎知識,為的是為后續講解利用bin log進行主從復制打下基礎,希望閱讀本文之后,您感到對二進制日志的理解在八股文的基礎之上,更進一步了。
我是白澤,一名后端程序員/學生黨,建了一個春秋招備戰/內推/閑聊群,歡迎大家加入。
下面是我的微信以及公眾號,關注公眾號【程序員白澤】,回復簡歷可以獲取我正在使用的簡歷模板。