4. undo日志
undo log(撤銷日志或回滾日志)記錄了事務發(fā)生之前的數(shù)據(jù)狀態(tài),分為insert undo log和update undo log。
如果修改數(shù)據(jù)時出現(xiàn)異常,可以用 undo log來實現(xiàn)回滾操作(保持原子性)??梢岳斫鉃閡ndo日志記錄的是反向的操作,比如INSERT操作會記錄DELETE,UPDATE會記錄UPDATE之前的值,和redo日志記錄在哪個物理頁面做了什么操作不同,所以這是一種邏輯格式的日志。
undo日志和redo日志與事務密切相關,被統(tǒng)稱為「事務日志」。
關于undo日志,我們目前只需要了解這么多即可
5. SQL更新語句的執(zhí)行總結——初版
有了事務日志之后,我們來簡單總結一下更新操作的流程,這是一個簡化的過程。
name 原值是chanmufeng
。
update t_user_innodb set name ='chanmufeng1994' where id = 1;
- 事務開始,從內存(Buffer Pool)或磁盤取到包含這條數(shù)據(jù)的數(shù)據(jù)頁,返回給 Server 的執(zhí)行器;
- Server 的執(zhí)行器修改數(shù)據(jù)頁的這一行數(shù)據(jù)的值為 chanmufeng1994;
- 記錄 name=chanmufeng 到undo log;
- 記錄 name=chanmufeng1994到redo log;
- 調用存儲引擎接口,記錄數(shù)據(jù)頁到Buffer Pool(修改 name=penyuyan);
- 事務提交。
6. binlog日志
之前我們講過,從MySQL整體架構來看,其實可以分成兩部分
- Server 層,它主要做的是 MySQL功能層面的事情,比如處理連接、解析優(yōu)化等;
- 存儲引擎層,負責存儲相關的具體事宜。
redo日志是InnoDB存儲引擎特有的日志,而Server層也有自己的日志,稱為 binlog(歸檔日志),它可以被所有存儲引擎使用。
6.1 為什么有了redo日志還需要 binlog?
我想你可能會問出這個問題,實際上,更準確的問法是為什么有了binlog還需要有redo日志?主要有以下幾個原因。
- 因為最開始MySQL里并沒有InnoDB存儲引擎。MySQL自帶的引擎是MyISAM,但是 MyISAM沒有崩潰恢復的能力,InnoDB后來以插件的形式被引入,順便帶來了redo日志;
- binlog日志是用來歸檔的,binlog以事件的形式記錄了所有的 DDL和 DML 語句(因為它記錄的是操作而不是 數(shù)據(jù)值,屬于邏輯日志),但是不具備宕機恢復的功能,因為可能沒有來得及刷新臟頁,造成臟頁數(shù)據(jù)的丟失,而這些操作也沒有保存到binlog中從而造成數(shù)據(jù)丟失;
- binlog記錄的是關于一個事務的具體操作內容,即該日志是邏輯日志。而redo日志記錄的是關于每個頁的更改的物理情況。功能壓根不是一回事兒。
6.2 binlog日志的作用
6.2.1 主從復制
binlog是實現(xiàn)MySQL主從復制功能的核心組件。
master節(jié)點會將所有的寫操作記錄到binlog中,slave節(jié)點會有專門的I/O線程讀取master節(jié)點的binlog,將寫操作同步到當前所在的slave節(jié)點。
6.2.2 數(shù)據(jù)恢復
假如你在閱讀這篇文章的時候覺得我寫得實在太好,拍案叫絕的時候一不小心把公司的數(shù)據(jù)庫給刪了,你該怎么做才能恢復到你刪庫之前的那個時刻的狀態(tài)?
這個時候就要用到binlog了,前提是binlog沒有被刪除,否則,神仙也救不了你了。
通常情況下,公司會定期對數(shù)據(jù)庫進行全量備份,可能隔一個月,一周,甚至可能每天都備份一次。運氣好的話你可以使用前一天的全量備份,恢復到前一天的某時刻狀態(tài)(或者一周、一月之前),然后從全量備份的時刻開始,從binlog中提取該時刻之后(前提是你的binlog里面存放了這段時間的日志)的所有寫操作(當然,你得過濾掉你的刪庫操作),然后進行操作回放就可以了。
是不是很簡單?
問題又來了。再看一眼我們的更新語句。
update t_user_innodb set name ='chanmufeng1994' where id = 1;
假如這條更新語句已經被寫入到了redo日志,還沒來得及寫binlog的時候,MySQL宕機重啟了,我們看一下會發(fā)生什么。
因為redo日志可以在重啟的時候用于恢復數(shù)據(jù),所以寫入磁盤的是chanmufeng1994。但是binlog里面沒有記錄這個邏輯日志,所以這時候用binlog去恢復數(shù)據(jù)或者同步到從庫,就會出現(xiàn)數(shù)據(jù)不一致的情況。
所以在寫兩個日志的情況下,就類似于「分布式事務」的情況,如果你不清楚分布式事務是個什么東西也沒關系,我在之后的文章會介紹到。能夠明確的就是redo日志和binlog日志如果單純依次進行提交是無法保證兩種日志都寫成功或者都寫失敗的。
我們需要「兩階段提交」。
6.3 兩階段提交
兩階段提交不是MySQL的專利,兩階段提交是一種跨系統(tǒng)維持數(shù)據(jù)邏輯一致性的常見方案,尤其在分布式事務上,所以請讀者重點體會思想
我們把redo日志的提交分成兩步,兩步中redo日志的狀態(tài)分別是prepare
和commit
。步驟如下
- InnoDB存儲引擎將更改更新到內存中后,同時將這個更新操作記錄到redo日志里面,此時redo日志處于
prepare
狀態(tài); - 執(zhí)行器生成這個操作的binlog,并將binlog刷盤;
- 執(zhí)行器調用InnoDB的提交事務接口,InnoDB把剛剛寫入的redo日志改成
commit
狀態(tài)。至此,所有操作完成。
加上兩階段提交之后我們再來看一下SQL更新語句的執(zhí)行流程。
7. SQL更新語句的執(zhí)行總結——終版
- 客戶端發(fā)送更新命令到MySQL服務器,經過處理連接、解析優(yōu)化等步驟;
- Server層向InnoDB存儲引擎要id=1的這條記錄;
- 存儲引擎先從緩存中查找這條記錄,有的話直接返回,沒有則從磁盤加載到緩存中然后返回;
- Server層執(zhí)行器修改這條記錄的name字段值;
- 存儲引擎更新修改到內存中;
- 存儲引擎記錄redo日志,并將狀態(tài)設置為
prepare
狀態(tài); - 存儲引擎通知執(zhí)行器,修改完畢,可以進行事務提交;
- Server先寫了個binlog;
- Server提交事務;
- 存儲引擎將redo日志中和當前事務相關的記錄狀態(tài)設置為
commit
狀態(tài)。
發(fā)布評論請先 登錄
相關推薦
評論