日誌系統

pinoky發表於2024-09-14

日誌系統

redo log重做日誌

redo log為InnoDB引擎特有的物理日誌,記錄了:“在某個資料頁上做了什麼修改” 的操作,迴圈寫入,具備著佔用空間小、順序寫磁碟的優點

img

write pos作為當前記錄的位置,一邊寫一邊順時針移動;checkpoint作為當前要擦除的位置,一邊擦出一邊順時針移動,在擦除記錄前要把資料落盤

write pos和checkpoint之間的空白 就可以用來記錄新的操作,如果write pos追上了checkpoint,就需要停止更新,將記錄落盤以推進checkpoint

透過redo log就能保證資料庫異常重啟後,之前提交的記錄不丟失,即crash-safe

redolog的寫入機制

在事務執行過程中,生成的日誌會寫入到redo log buffer,直到最終真正執行commit 的時候才會將redo log buffer中的日誌寫入到redo log檔案中

img

redo log可能有的三種狀態:存在buffer中(記憶體);write到磁碟但沒有fsync持久化(page cache);持久化(磁碟)

redo log寫入到redo log buffer

在實際的過程中,redolog是以的形式寫入到redo log buffer中的,即是以一個mtr裡所有的redo日誌為基本單位整體寫入的

每組redo日誌都對應著一個LSN值,即日誌邏輯序列號,表示當前系統寫入buffer中 的redo log量(LSN = 當前所佔用的所有redo log buffer的空間總和),每次寫入長度為length的redo log,LSN的值就會再加上length,它也會被寫入到InnoDB資料頁中,用來確保資料頁不會多次執行重複的redo log

如果有多個併發事務,當trx1開始寫盤時,此時組裡已有trx2和trx3(LSN=160),那麼trx1寫盤時帶的值就是LSN=160(此時的redo log buffer裡面已經包含到LSN=160的redo log了);因此等trx1返回時,所有LSN<160的redo log已經被持久化了,這時候的trx2和trx3可以直接返回

在這種組提交機制下,組員越多,節約磁碟IOPS的效果越好,

redo log buffer刷盤到磁碟

事務執行期間如果MySQL發生異常重啟,則redo log buffer會丟失其中的redo log,但由於事務沒有提交,所以丟失redo log並沒有影響

InnoDB有一個後臺執行緒,每隔1秒就會把redo log buffer中的日誌,呼叫write寫入到檔案系統的page cache,然後呼叫fsync持久化到磁碟

由於事務執行過程中的redo log也會直接寫在redo log buffer中,這些redo log也會被後臺執行緒一起持久化到磁碟,所以一個沒有提交事務的redo log也有可能已經持久化了

同時,redo log buffer 佔用的空間即將達到 innodb_log_buffer_size 一半的時候,後臺執行緒會主動寫盤,且並行的事務提交的時候,順帶將這個事務的 redo log buffer 持久化到磁碟

可以透過引數innodb_flush_log_at_trx_commit控制redo log寫入策略

  • = 0:每次事務提交只是把redo log 留在 redo log buffer
  • = 1:每次事務提交都將redo log 持久化(在兩階段提交中的prepare階段 即 fsync,commit階段只write)
  • = 2:每次事務提交都只把redo log寫到page cache

binlog歸檔日誌

binlog是MySQL的Server層實現的邏輯日誌,記錄語句的原始邏輯,比如:”給ID=2這一行的c欄位+1“,作為追加寫的日誌,binlog寫到一定大小後會切換到下一個,不會覆蓋之前的日誌

binlog的格式

statement格式的binlog最後有COMMIT,row格式的binlog最後有XID event,同時引入binlog-checksum引數,透過這三個方面可以讓MySQL驗證binlog的完整性

statement格式下的binlog日誌就是儲存著執行的語句原文,如:img

但在這種格式下,因為delete帶著limit,可能會因為刪除選取的索引不同,而導致用於主備時導致主備資料不一致

row格式下的binlog日誌記錄了server id,table_map(在哪個庫上面做的操作)以及真正操作的行主鍵id,如:img

這就避免了statement格式下可能導致的主備資料不一致的問題,在越來越多的場景下使用row都更方便資料恢復

但row格式下的binlog日誌耗費空間嚴重,同時耗費傳輸binlog時的IO資源影響執行速度,所以出現了兼備statement格式和row格式的mixed格式

單純的binlog是 不具備 crash-safe 的,原因是:binlog沒有能力恢復資料頁,由於WAL技術,執行事務寫完記憶體和日誌,事務就算完成了,如果發生崩潰需要依賴日誌來恢復資料頁,因為binlog並沒有記錄資料頁的更新細節,系統很難判斷需要從哪個事務的binlog開始重放

相比redo log,binlog用處主要有2個

  • 歸檔:相比redolog的迴圈寫,binlog是追加寫,可以保留歷史日誌
  • 複製:作為實現主從複製一部分的binlog二進位制日誌,可以支援從庫做增量複製

利用binlog做資料恢復:系統定期做整庫備份,需要資料恢復時找到最近一次全量備份,然後從備份的時間點開始以此取出binlog,重放到想要的時刻

binlog的寫入機制

事務執行過程中,先把日誌寫到binlog cache,提交時再把binlog cache寫入到binlog檔案中,一個事務的binlog不能拆開,需要確保一次性寫入

每個執行緒都有自己的binlog cache,但共用一份binlog filesimg

每個執行緒透過write將日誌寫到檔案系統的page cache;隨後再透過fsync將binlog files持久化到磁碟(此時才佔磁碟的IOPS)

可以透過引數sync_binlog控制write和fsync的時機

  • = 0 每次commit 事務只write,不fsync;
  • = 1每次都會fsync;
  • =N,累計N個事務後才fsync

在出現IO瓶頸時,會將它設定為一個比較大的值提升效能,但會有丟失最近N個事務的binlog日誌的風險

WAL 與 兩階段提交

update T set c=c+1 where ID=2;的執行過程如下

img

引擎將新資料更新到記憶體中,會先把更新操作記錄到redo log,令其處於prepare狀態,然後告知執行器執行完成,可提交事務;執行器生成該操作的binlog並寫入磁碟;執行器提交事務,引擎將剛才的redo log改成commit狀態

兩階段提交:將redo log的寫入拆成了兩個步驟 prepare 和 commit,由於redolog 和 binlog 都可以用於表示事務的提交狀態,所以兩階段提交的目的就是保證兩個日誌之間的邏輯一致

如果沒有兩階段提交,就要麼先寫入redolog再寫binlog,或先寫binlog再寫redolog,如果在寫完一個日誌後系統崩潰,就會導致主從資料庫不一致、恢復後資料與原資料不一致的情況發生

binlog和redolog都有一個資料欄位:XID,基於兩階段提交進行崩潰恢復

  • 順序掃描redo log,碰到既有prepare又有commit的redo log,說明該事務已經順利commit,則直接提交事務
  • 如果碰到只有prepare沒有commit的redo log,就用XID去找到該事務對應的binlog,如果binlog是完整的,則提交事務,否則回滾事務

組提交機制最佳化:實際上會將redo log的fsync 以及 binlog 的fsync 放在一起,讓一次fsync所帶的組員更多,從而實現binlog的組提交img

WAL:write-ahead-logging,關鍵點在於先寫日誌,再寫磁碟,當一條記錄需要更新的時候,InnoDB會先把記錄寫到日誌中並更新記憶體,此時視為更新完成,直至系統空閒時將這個操作記錄寫入磁碟,提高磁碟IO效率的原因來自:

  • redo log和binlog都是順序寫,磁碟的順序寫比隨機寫速度要快
  • 組提交機制,可以大幅度降低磁碟的IOPS消耗

相關文章