上一篇簡單的介紹了下MVCC(多版本並發控制)的原理,MVCC會對事物內操作的數據做多版本控制,從而實現並發環境下事物對數據寫操作的阻塞不影響讀操作的性能。而這個多版本控制的實現是由undo log來實現的,下面的內容將會簡單的介紹下undo log的內容。
mysql在事物開始操作數據之前,會先將原始數據備份到一個undo log的地方,這樣做的目的有兩個。第一是為了保證事物的原子性,如果事物在執行的過程中出現了某些錯誤,或者是用戶執行了rollback的操作,mysql可以利用undo log中的備份將數據恢復到事物開始之前的狀態。第二是為了實現多版本的並發控制,事物在提交之前,undo log中保存了未提交之前的數據版本,undo log可以作為舊版數據的快照供其他並發訪問的事物實現快照讀。
快照讀:SQL讀取的數據是快照版本,也就是歷史版本,普通的select查詢的就是快照。innodb存儲引擎的快照讀,讀取的數據將由cache(原始數據)+undo(事物修改過的數據)兩部分組成。
當前讀:SQL讀取的數據是最新版本,可以通過鎖的機制來保證讀取的數據無法被其它的事物修改。update,delete,insert,select ... lock in share mode,select ... for update都是當前讀。
除了undo log,Mysql數據庫還有一個redo log的概念,mysql在事物開始之后,事物中操作的任何數據,會將最新的數據備份到一個地方(redo log),就是在事物執行的過程中,開始將數據寫入redo buffer中,最后寫入redo log中,具體的落盤策略可以自行配置。這樣做的目的是:為了實現事物的持久性,防止在發生故障的時間點,尚有臟頁未寫入磁盤,在mysql重啟的時候,根據redo log重做,從而使事物未入磁盤的數據達到持久化這一特定。
說明:指定的redo log是記錄在{datadir}/ib_logfile1&ib_logfile2,可以通過innodb_log_group_home_dir配置指定的目錄存儲。一旦事物提交成功,並且數據持久化落盤之后,redo log中的數據也就失去了意義,所以redo log中的寫入是日志文件循環寫入的。可以配置下列參數:
指定redo log日志文件組中的數量 innodb_log_files_in_group 默認為2
指定redo log每一個日志文件最大存儲量innodb_log_file_size 默認48M
指定redo log在cache/buffer中的buffer池大小innodb_log_buffer_size 默認16M
redo log的落盤策略,即將數據從redo buffer持久化到磁盤的redo log文件中,可配置Innodb_flush_log_at_trx_commit參數,這個參數的取值有以下幾種情況。
取值0,每秒提交redo buffer -> redo log os cache -> flush cache to disk,這種情況可能丟失一秒內的事物數據。
取值1,該參數的默認值,每次事物提交執行redo buffer -> redo log os cache -> flush cache to disk,這種情況可能丟失一個事物的數據,最安全,但同時性能最差。
取值2,每次事物提交執行redo buffer -> redo log os cache,在每一秒執行 -> flush cache to disk操作。
關於undo log和redo log,有下面的圖可以參考。
說明:undo log和redo log可以理解為都是對數據的備份,其中undo log是對舊數據(原始數據)的備份,redo log是對新數據(事物所要提交的數據)的備份。對數據的備份首先是會存儲在各自的buffer中,即undo buffer和redo buffer,至於如何持久化到磁盤,即寫入到undo log和redo log文件中,要根據設置的具體落盤策略而定。