InnoDB原子性和持久性
- 數(shù)據(jù)庫的原子性包括兩個內(nèi)容:災(zāi)難恢復(fù)和事務(wù)回滾。InnoDB通過redo日志來支持災(zāi)難恢復(fù),通過undo日志來支持事務(wù)回滾。
- 數(shù)據(jù)庫的持久性指事務(wù)一旦commit就會被持久化:通過在commit時同步寫入數(shù)據(jù)庫來支持。
redo日志:實(shí)現(xiàn)災(zāi)難恢復(fù)

InnoDBRedo
- 為了更好的進(jìn)行持久化,innodb使用WAL方式進(jìn)行持久化,WAL通過將對數(shù)據(jù)的變更以追加的形式寫入日志,并且在之后將變更日志應(yīng)用到數(shù)據(jù)上,來將隨機(jī)IO盡可能地轉(zhuǎn)化為批量IO,在innodb中,這個叫做redo日志。
- 另外,為了增加頻繁申請WALBuffer,一般都會預(yù)設(shè)一個環(huán)形內(nèi)存Buffer區(qū)進(jìn)行重用,在innodb中,這個叫做redo log buffer。
- innodb存在兩種類型的redo日志:物理日志直接記錄對頁的修改,而邏輯日志則記錄邏輯操作,為了保證redo日志的冪等性,意味著,對于同樣狀態(tài)的物理頁,應(yīng)用邏輯日志或者物理日志后生成的頁面數(shù)據(jù)應(yīng)該是一樣的。
- 在用戶線程中,一個事務(wù)包含多個語句,每個語句包含多個MTR,一個MTR是對底層頁面的一次完整訪問,由多個Redo日志組成;每次向redo log buffer寫入的基本粒度是一個MTR,這也是InnoDB引擎支持的最小并發(fā)粒度;redo log被劃分為很多block,每個block的大小為512字節(jié),等于一個扇區(qū)的大?。辉赾ommit或者其它時機(jī),redo log被刷入磁盤。
- lsn為內(nèi)存緩沖區(qū)已寫入的字節(jié)數(shù),flushed_lsn為已刷新到磁盤的字節(jié)數(shù),ckpt_lsn為已經(jīng)應(yīng)用到磁盤數(shù)據(jù)上的字節(jié)數(shù)。
- ckpt_lsn是通過FlushList進(jìn)行更新,F(xiàn)lushList按page修改的最小的lsn進(jìn)行排序,每當(dāng)隊(duì)尾page刷新到磁盤上時,ckpt_lsn都進(jìn)行更新。
- 崩潰恢復(fù):通過ckpt_lsn,可以確定崩潰恢復(fù)的起點(diǎn),通過依次讀取redolog直到一個不足512字節(jié)的block,則為崩潰恢復(fù)的終點(diǎn),之后只要重新應(yīng)用redolog到磁盤數(shù)據(jù)即可。通過比較page的FIL_PAGE_LSN和將對同一個page的redo日志行為進(jìn)行劃分合并,可以減少恢復(fù)的時間。
undo日志:回滾和MVCC

INNODBUndo
- B+樹索引數(shù)據(jù)中通過roll_pointer指向undo日志,uodate和delete類型的日志還有roll_pointer,指向更早的undo日志,從而形成版本鏈,索引數(shù)據(jù)和undo日志中的trx_id用來實(shí)現(xiàn)不同的隔離級別,兩者結(jié)合,實(shí)現(xiàn)支持不同隔離級別的MVCC。
- 所有的undo日志都保存在undo日志段中,共有128個日志段,每個段中有1024個undo日志鏈,通過Page5中,可以找到128個undo日志段描述頁,通過undo日志段描述頁中的undoslots,可以找到1024個日志鏈,每個日志鏈中包含多個undo日志頁,以鏈表形式連接起來,第一個undo日志頁中不僅記錄undo日志,還記錄有undo日志鏈信息。
- 每個事務(wù)最多會使用4個日志鏈:普通表insert undo鏈;臨時表update undo鏈;普通表insert undo鏈;臨時表update undo鏈。劃分四個不同類型的鏈的原因是:臨時表不需要寫redo日志,insert日志在事務(wù)提交后可直接釋放,而update日志在事務(wù)提交后因需要支持MVCC不能直接釋放而需要等待purge線程清理后再釋放。
- 日志鏈滿足僅包含一個undo日志且使用量少于3/4時在事務(wù)提交后可以被重用,重用時,對于insert日志直接覆蓋寫,對于uodate日志,則通過使用UndoLogHeader中的nextLog和prevLog繼續(xù)增加新的Undo鏈信息追加寫。
- 為事務(wù)分配undo日志鏈的過程:
- 以輪轉(zhuǎn)方式從128個日志段中分配日志段
- 如果分配到的日志段中有可重用的日志鏈,則重用
- 若無,則以輪轉(zhuǎn)方式從1024個日志鏈中分配日志鏈