瞭解了 SQL 執行的流程,知道每一條語句都經過聯結器、查詢儲存、分析器、優化器、執行器最後到儲存引擎的過程。查詢語句是如此,更新語句也不例外。
不同的是,更新語句會修改表資料,這裡就涉及到兩個重要的紀錄檔模組 redolog 和 binlog。
本篇還是選用 InnoDB 搜尋引擎。
下面引入丁奇的經典比喻。
《孔乙己》這篇文章中,酒店掌櫃有一個粉板,專門用來記錄客人的賒賬記錄。
如果賒賬的人不多,他可以把顧客名和賬目寫在板上。
但如果賒賬的人多了,粉板總會有記不下的時候,這個時候掌櫃一定還有一個專門記錄賒賬的賬本。
如果有人要賒賬或者還賬的話,掌櫃一般有兩種做法:
一是直接把賬本翻出來,把這次賒的賬加上去或者扣除掉;
另一種是先在粉板上記下這次的賬,等打烊以後再把賬本翻出來核算。
在生意紅火櫃檯很忙時,掌櫃一定會選擇後者,因為前者操作實在是太麻煩了。
首先,你得找到這個人的賒賬總額那條記錄。你想想,密密麻麻幾十頁,掌櫃要找到那個名字,可能還得帶上老
花鏡慢慢找,找到之後再拿出算盤計算,最後再將結果寫回到賬本上。
你想想,如果掌櫃沒有粉板的幫助,每次記賬都得翻賬本,效率是不是低得讓人難以忍受?
上面的比喻非常形象地說明了 Mysql 中的 WAL(Write-Ahead-Logging) 技術。
先寫紀錄檔(redolog),再寫磁碟。也就是先寫粉板再寫賬本。
redolog 是 InnoDB 引擎特有的紀錄檔。
當有資料要更新時,InnoDB 會先記錄 redolog,同時更新記憶體,更新操作就結束了。等到合適的時候(空閒或者粉板寫滿了),再更新磁碟。
redolog 有固定大小,就像固定大小的粉板,寫滿就得回到開頭回圈寫。
write pos 是當前記錄粉板的位置,一邊寫一邊往後移。
check point 是擦除的位置,擦除前會把更新記錄到資料檔案。
redolog 保證了資料庫異常重啟時資料不丟失,這個能力被稱為 crash-safe。
(丁奇原圖)
mysql> show variables like "innodb_log_group_home_dir";
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| innodb_log_group_home_dir | ./ |
+---------------------------+-------+
1 row in set (0.00 sec)
mysql> show variables like "innodb_log_file%";
+---------------------------+----------+
| Variable_name | Value |
+---------------------------+----------+
| innodb_log_file_size | 33554432 |
| innodb_log_files_in_group | 2 |
+---------------------------+----------+
2 rows in set (0.00 sec)
mysql> show variables like "innodb_flush_log_at_trx_commit";
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set (0.00 sec)
binlog 是 Server 層的紀錄檔,叫歸檔紀錄檔。不同於 redolog 只有 InnoDB 有,binlog 任何引擎都可以使用。
binlog 是邏輯紀錄檔,記錄語句的原始邏輯;redolog 是物理紀錄檔,記錄資料頁的修改內容。
binlog 不會迴圈寫,一個寫完後切換到寫一個;redolog 有固定的大小。
binlog 在 data 目錄下,mysql-bin.xxxxxx。
設定 binlog 每次事務直接持久化到磁碟上,保證重啟後資料不丟失。
mysql> show variables like "sync_binlog";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog | 1 |
+---------------+-------+
1 row in set (0.00 sec)
update T set c=c+1 where ID=2;
執行器呼叫引擎介面找 ID=2 的資料,如果 ID=2 資料所在的資料頁在記憶體中,則直接返回給執行器。否則先從磁碟讀取再返回。
執行器拿到資料,把 c 加 1,得到新的一行資料。然後呼叫引擎介面,寫入這行資料。
引擎將新資料更新到記憶體中,順便記錄 redolog,此時 redolog 處於 prepare 狀態,然後告訴執行器可以提交事務。
執行器把 binlog 寫入磁碟,然後呼叫引擎介面提交事務。redolog 狀態變為 commit。更新完成。
既然記錄了 log,那麼當資料誤操作後,就可以根據資料庫定期備份,加上記錄的 log 方便的恢復到誤操作之前的狀態。
先根據備份恢復到臨時表
根據 binlog 找到備份後到誤操作前的記錄。
把恢復好的臨時庫按需要同步到正式庫中。
只要有 log,就可以恢復到任何時刻的資料狀態。
再也不用擔心誤操作無法恢復了。