Mysql 日誌系統
bin-log & redo-log & undo log
1. 避免從刪庫到跑路 - bin log
1.1 什麼是bin log
- binlog 即二進位制日誌,他記錄了引起或可能引起資料庫改變事件,包括事件發生的時間、開始位置、結束位置等資訊,select、show 等查詢語句不會引起資料庫改變,因此不會被記錄在 binlog 中
- 對於事務的執行,只有事務提交時才會一次性寫入 binlog,對於非事務操作,則每次語句執行成功後都會直接寫入 binlog
- 因此,基於 binlog,我們可以看到每一次對資料庫的修改是在何時以何種方式執行的,從而可以實現對任意條操作的回滾,當然
- mysql 的主從同步機制也是依賴 binlog 來實現的,binlog 讓從資料庫可以精準還原主庫的每一個操作
1.2 bin log
結構
binlog
是可以追加寫入的,追加寫入指的是binlog
檔案寫到一定大小後會切換到下一個檔案,並不會覆蓋以前的檔案
1.3 如何靠bin log
恢復資料
bin log
會記錄所有的邏輯操作,並且採用“追加寫”的形式,如果你的DBA承諾說半個月內可以恢復,那麼備份系統中一定會儲存最近半個月的所有bin-log
,同時系統會定期做整庫備份
當需要恢復到指定的某一秒時,比如某天下午兩點發現中午12點有一次誤刪表,需要找回資料,那麼可以這麼做
- 首先,找到最近的一次去全量備份,如果你運氣好,可能就是昨天晚上的一個備份,從這個備份恢復到臨時庫
- 然後,從備份的時間點開始,將備份的
binlog
一次取出來,重放到中午誤刪表之前的那個時刻
這樣你的臨時庫就和誤刪之前的線上庫一樣了,然後你可以把表資料從臨時庫中求出來,按需求恢復到線上庫中
2. 異常情況下的事務安全 - 重做日誌redo log
2.1 更新操作是否應該直接操作磁碟資料?
對於每次更新來說,最簡單的方法就是每次都把操作記錄到磁碟,去磁碟找相應的資料,再進行更新,但這樣頻繁的IO
操作會導致效能的下降
2.2 WAL
技術
再同一事務中,當有記錄需要更新時,InnoDB引擎將修改結果更新到記憶體後,會在redo log
新增一行記錄來記錄“需要在哪個資料頁上做什麼修改”,並將該記錄的狀態置為prepare
commit
提交事務後,會將此次事務中在redo log
新增的記錄的狀態都置為commit
狀態,同時,InnoDB引擎會在適當的時候,將redo log
中狀態為commit
的記錄的修改更新到磁盤裡面,而這個更新往往是在系統比較空閒的時候做
這樣的操作叫做Write Ahead Logging
,他的關鍵在於先寫日誌,再寫磁碟
寫日誌也是在磁碟上的寫操作,為什麼比直接在磁碟持久化資料高效?
WAL是順序寫入的,也就是一直在檔案末尾append,而持久化資料庫的資料是一個隨機寫入的操作,順序寫會節省大量磁碟懸臂來回定址的過程,效率更高
現在是否還需要WAL?
現在都用SSD而不在使用HARD,SSD沒有機械結構,無需尋道,那麼上面所說的優點是否就不存在了?
2.3 redo-log
的結構
InnoDB 的 redo log
是固定大小的,比如可以配置為一組 4 個檔案,每個檔案的大小是 1GB,那麼這塊“粉板”總共就可以記錄 4GB 的操作。從頭開始寫,寫到末尾就又回到開頭迴圈寫
write pos
是當前記錄的位置,一邊寫一邊後移,寫到第 3 號檔案末尾後就回到 0 號檔案開頭checkpoint
是當前要擦除的位置,也是往後推移並且迴圈的,擦除記錄前要把記錄更新到資料檔案write pos
和checkpoint
之間的是“粉板”上還空著的部分,可以用來記錄新的操作。如果write pos
追上checkpoint
,表示“粉板”滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint
推進一下
2.4 crash-safe
有了 redo log
,InnoDB 就可以保證即使資料庫發生異常重啟,之前提交的記錄都不會丟失,當資料庫發生宕機重啟後,可以通過redo log
將未落盤的資料恢復,這個能力稱為 crash-safe
2.5 redo log
是如何保證crash safe
的
每條 redolog 都有兩個狀態 – prepare 與 commit 狀態
例如對於一張 mysql 表
(CREATE TABLE `A` (`ID` int(10) unsigned NOT NULL AUTO_INCREMENT, `C` int(10) NOT NULL DEFAULT 0, PRIMARY KEY (`ID`)) ENGINE=InnoDB)
我們執行一條 SQL 語句:
mysql> update T set c=c+1 where ID=2
- 執行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜尋找到這一行。如果 ID=2 這一行所在的資料頁本來就在記憶體中,就直接返回給執行器;否則,需要先從磁碟讀入記憶體,然後再返回。
- 執行器拿到引擎給的行資料,把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的一行資料,再呼叫引擎介面寫入這行新資料。
- 引擎將這行新資料更新到記憶體中,同時將這個更新操作記錄到 redo log 裡面,此時 redo log 處於 prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。
- 執行器生成這個操作的 binlog,並把 binlog 寫入磁碟。
- 執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成
(圖中淺色框表示是在InnoDB內部執行的,深色框表示在執行器中執行的)
可以看到,在寫入 binlog 及事務提交前,innodb 先記錄了 redolog,並標記為 prepare 狀態,在事務提交後,innodb 會將 redolog 更新為 commit 狀態,這樣在異常發生時,就可以按照下面兩條策略來處理:
- 當異常情況發生時,如果第一次寫入 redolog 成功,寫入 binlog 失敗,MySQL 會當做事務失敗直接回滾,保證了後續 redolog 和 binlog 的準確性
- 如果第一次寫入 redolog 成功,binlog 也寫入成功,當第二次寫入 redolog 時候失敗了,那資料恢復的過程中,MySQL 判斷 redolog 狀態為 prepare,且存在對應的 binlog 記錄,則會重放事務提交,資料庫中會進行相應的修改操作