MySQL 8 0 redo log的深入解析

2022-09-24 10:24:12 字數 2830 閱讀 4419

最開始了解mysql實現的時候,總聽到redo log, wal(write-ahead logging),undo log這些關鍵詞,了解到redo log主要是用於實現事務的持久化的。為了進一步了解redo log,看了下相關**(原始碼版本: mysql 8www.cppcns.com.0.12),這裡簡單總結下,主要介紹redo log是如何產生,如何落盤,以及最終通知使用者的。

讀寫事務在執行的過程中,會不斷的產生redo log。申請資料頁、修改資料頁、記錄undo log等,都會產生redo log。mysql將使用者事務拆分成乙個個mtr(mini transaction),redo log最初產生時就是被記錄到mtr中的,並伴隨著mtr的提交而提交,最終落到硬碟上。

mtr在提交時,會將mtr中的redo log寫到系統變數log_sys的log buffer中。mysql8.0乙個新特性就是redo log提交的無鎖化。在8.0以前,各個使用者執行緒都是通過互斥量競爭,序列的寫log buffer,因此能保證lsn的順序無間隔增長。8.0時使用者執行緒可以併發寫log buffer,如果某個使用者執行緒寫log buffer成功後,就將自己寫的lsn以前的log buffer刷盤,則有可能導致其他使用者執行緒寫log buffer還沒完成就被刷盤。

為了解決這個問題,mysql 8.0引入了link_buf這個資料結構來避免log buffer的空洞。link_buf實際是乙個定長陣列,像滑動視窗一樣跟蹤log buffer一段區間的寫入情況,隨著log buffer中寫入連續redo log不斷向前推進。

link_buf的資料結構如圖:

當使用者在log buffer的start_lsn-end_lsn間寫下redo log時,會標記link_buf相應的位置,即將m_link[start_lsn%m_capacity]賦值為為end_lsn-start_lsn。

redo log記錄到log buffer的過程如下:

1.首先,各使用者執行緒寫redo log時,先根據redo log長度,向系統全域性原子變數log_sys.sn獲取本次redo log日誌的start_lsn, end_lsn。原子變數sn能保證各執行緒獲得的start_lsn-end_lsn區間連續無空洞;

2.使用者執行緒申請到start_lsn-end_lsn區間後,需要先等待到link_buf推進到自己可以使用的位置。

如圖所示,start_lsn0-end_lsn0,start_lsn2-end_lsn2, start_lsn3-end_lsn3為三個使用者執行緒新申請的lsn區間;start_lsn1-end_lsn1對應的區間已經標記到link_buf上;start_lsn3-end_lsn3距離tail太遠,需要等待link_buf推進才能使用;

3.寫入log buffer後,再將start_lsn->end_lsn的範圍標記到link_buf(注意:因為只在startwww.cppcns.com_lsn%capacity的位置標記link_buf,所以即使end_lsn超過(m_tail, m_tail+m_capacity)也不影響);

4.使用者執行緒提交事務時設定事件log_sys.writer_event,觸發log_writer執行緒將日誌從redo log buffer寫到系統快取(log_writer執行緒自己也會輪詢link_buf判斷是否寫入了新的日誌);

5.log_writer執行緒推進m_tail,並將m_tail前的log buffer落盤。

前面簡述了redo log是如何提交的,在redo log提交以及落盤時,涉及多個執行緒,他們的關係如下:

使用者執行緒在讀寫事務提交時,會產生一些redo log,並隨著mtr提交而記錄到redo log buffer中,隨後使用者執行緒嘗試設定writer_event觸發log_writer執行緒寫日誌,並監聽屬於自己的flush_events[i]事件;

log_writer執行緒推進link_buf.m_tail,將最大連續lsn前的redo log寫入系統快取,並設定flusher_event觸發log_flusher執行緒;

log_flusher執行緒將已寫入系統快取的日誌刷盤,並設定flush_notifier_event觸發log_flush_notifier執行緒通知使用者;

log_flush_notifier根據已刷盤的lsn換算出需要觸發的事件,通知使用者執行緒。

具體實現時,通過log_sys中的幾個成員變數,跟進redo log的寫入情況。其中log_sys.recent_writtern.m_tail表示log buffer最大連續範圍;log_sys.write_lsn表示寫入到系統快取的位置;log_sys.flushed_to_disk_lsn表示已落盤的位置。各標記的推進過程如下:

使用者提交事務時,會根據innodb_flush_log_at_trx_commit引數,呼叫log_wait_for_write或log_wait_for_flush,來等待redo log寫入到系統快取或刷到硬碟。使用者執行緒的通知是通過log_sys.flush_events事件陣列來實現的,為了避免一次通知的flush_events過多,flush_events會像桶一樣劃分給不同的使用者執行緒:redo log是以乙個個log block劃分的,假設log_sys.flush_events陣列長度為m,則第n個log block的刷盤,由flush_events[n%m]事件監聽。當log buffer的第l1個log block到第l2個log block被刷盤時,會設定l1-l2之間的log block所屬的flush_events,從而redo log在l1-l2之間的使用者執行緒都會收到通知。

mysql8.0通過redo log無鎖化,解決了使用者執行緒寫redo log時競爭鎖帶來的效能影響。同時將redo log寫檔案、redo log刷盤從使用者執行緒中剝離出來,抽成單獨的執行緒,使用者執行緒只負責將redo log寫入到log buffer,不再關心redo log的落盤細節,只需等待log_writer執行緒或log_flusher執行緒的通知。

MySQL 重做日誌 redo log 原理

redo log buffer redo log file 原理 目錄 1.重做日誌寫入過程圖 2.相關知識點彙總圖 3.redo log buffer 原理 4.redo log file 原理 1.重做日誌寫入過程 3.redo log buffer 原理 重做日誌緩衝 redo log buf...

mysql 日誌之redo log 重做日誌

redo log 具體來說,當有一條記錄需要更新的時候,innodb 引擎就會先把記錄寫到 redo log 檔案 裡面,並更新記憶體,這個時候更新就算完成了。同時,innodb 引擎會在適當的時候,將這個操作記錄更新到磁碟裡面,而這個更新往往是在系統比較空閒的時候做。mysql 裡經常說到的 wa...

mysql8 0提示命令 MySQL8 0操作命令

mysql8.0版本和mysql5.0的加密規則不一樣,而現在的很多任務具等都是不支援的,我們這裡使用的是將mysql使用者登入的加密規則修改為mysql native password的方法來進行解決的。修改加密規則alter user root localhost identified by p...