日志系統(tǒng): redo log(重做日志)和binlog(歸檔日志)。
重要的日志模塊:redo log
MySQL WAL(Write-Ahead-Logging)關(guān)鍵點(diǎn)是先寫日志,再寫磁盤。
當(dāng)更新數(shù)據(jù)時(shí),InnoDB引擎就會(huì)先把記錄寫到redo log里面,并更新內(nèi)存,這個(gè)是否更新就算完成,在合適的時(shí)候,將記錄寫入到磁盤里面。
redo log的標(biāo)記:write pos 是當(dāng)前記錄的位置,一邊寫一邊后羿;checkpoint是當(dāng)前要擦出的位置,也是往后移動(dòng)推移并且循環(huán)的,擦除記錄前要把記錄更新到數(shù)據(jù)文件。有了redo log,InnoDB就可以保證即使數(shù)據(jù)庫(kù)發(fā)生異常重啟,之前提交的記錄都不會(huì)丟失,這個(gè)能力成為crash-safe。
重要的日志模塊:binlog
Server層有自己的日志,稱為binlog。redo log是InnoDB引擎特有的日志。
剛開始時(shí),MySQL自帶的引擎是MyISAM,但是MyISAM沒有crash-safe的能力,binlog日志只能用于歸檔。
redo log與binlog的區(qū)別:
- redo log是InnoDB引擎特有的;binlog是MySQL Server層實(shí)現(xiàn)的,所有引擎都可以使用。
- redo log是物理日志,記錄的是在某個(gè)數(shù)據(jù)頁(yè)上做了什么修改;binlog是邏輯日志,記錄的是這個(gè)語句的原始邏輯。
- redo log是循環(huán)寫的,空間固定會(huì)用完;binlog是可以追加寫入的。追加寫入是指binlog文件寫到一定大小后會(huì)切換到下一個(gè),并不會(huì)覆蓋以前的日志。
更新流程:
- 執(zhí)行器先找引擎得到修改的記錄,如果在內(nèi)存中,直接返回;如果沒有在內(nèi)存中,從磁盤讀取內(nèi)存,然后在返回。
- 執(zhí)行器拿到數(shù)據(jù)后,修改之后,然后通過引擎將數(shù)據(jù)寫入內(nèi)存。
- 引擎將數(shù)據(jù)更新到內(nèi)存中,同時(shí)將這個(gè)更新操作記錄到redo log里面,此時(shí)redo log處于prepare狀態(tài),然后告知執(zhí)行器執(zhí)行完成了,隨時(shí)可以提交事務(wù)。
- 執(zhí)行器生成這個(gè)操作的binlog,并把binlog寫入磁盤。
- 執(zhí)行器調(diào)用引擎的提交事務(wù)接口,引擎把剛剛寫入的redo log改成commit狀態(tài),更新完成。
兩階段
binlog會(huì)記錄所有的邏輯操作,并且是采用"追加寫"的形式。
由于redo log和binlog是兩個(gè)獨(dú)立的邏輯,如果不用兩階段,就會(huì)出現(xiàn)問題
- 先寫redo log后寫binlog:redo log寫完,binlog沒有寫完,MySQL進(jìn)程異常重啟。系統(tǒng)崩潰之后,恢復(fù)的數(shù)據(jù)為更新后的數(shù)據(jù),binlog恢復(fù)的數(shù)據(jù)是原先的數(shù)據(jù)。
- 先寫binlog 后寫redo:binglog寫完之后crash,事務(wù)無效,值沒有變化,由于redo log還沒寫,通過binlog恢復(fù)的數(shù)據(jù)就多一個(gè)事務(wù)出來,與原庫(kù)不同。
redo log用于保證crash-safe能力。innodb_flush_log_at_trx_commit這個(gè)參數(shù)設(shè)置為1的時(shí)候,表示每次事務(wù)的redo log都直接持久化到磁盤。
這樣可以保證MySQL異常重啟之后數(shù)據(jù)不丟失。
sync_binlog這個(gè)參數(shù)設(shè)置為1時(shí),表示每次事務(wù)的binlog都持久化到磁盤。這樣保證MySQL異常重啟之后binlog不丟失。
binlog有兩種模式,statement格式的記sql語句,row格式會(huì)記錄行的內(nèi)容,記兩條,更新前和更新后都有。
redo只是完成了prepare,binlog失敗,事務(wù)本身就會(huì)回滾,所以這個(gè)庫(kù)里面status的值是0。