MySQL 8.0 redo log的深入解析
前言
最開始了解mysql實現(xiàn)的時候,總聽到redo log, WAL(write-ahead logging),undo log這些關(guān)鍵詞,了解到redo log主要是用于實現(xiàn)事務(wù)的持久化的。為了進(jìn)一步了解redo log,看了下相關(guān)代碼(源碼版本: mysql 8.0.12),這里簡單總結(jié)下,主要介紹redo log是如何產(chǎn)生,如何落盤,以及最終通知用戶的。
redo log的產(chǎn)生
讀寫事務(wù)在執(zhí)行的過程中,會不斷的產(chǎn)生redo log。申請數(shù)據(jù)頁、修改數(shù)據(jù)頁、記錄undo log等,都會產(chǎn)生redo log。mysql將用戶事務(wù)拆分成一個個mtr(mini transaction),redo log最初產(chǎn)生時就是被記錄到mtr中的,并伴隨著mtr的提交而提交,最終落到硬盤上。
redo log 的提交
mtr在提交時,會將mtr中的redo log寫到系統(tǒng)變量log_sys的log buffer中。mysql8.0一個新特性就是redo log提交的無鎖化。在8.0以前,各個用戶線程都是通過互斥量競爭,串行的寫log buffer,因此能保證lsn的順序無間隔增長。8.0時用戶線程可以并發(fā)寫log buffer,如果某個用戶線程寫log buffer成功后,就將自己寫的lsn以前的log buffer刷盤,則有可能導(dǎo)致其他用戶線程寫log buffer還沒完成就被刷盤。

為了解決這個問題,mysql 8.0引入了Link_buf這個數(shù)據(jù)結(jié)構(gòu)來避免log buffer的空洞。Link_buf實際是一個定長數(shù)組,像滑動窗口一樣跟蹤log buffer一段區(qū)間的寫入情況,隨著log buffer中寫入連續(xù)redo log不斷向前推進(jìn)。
Link_buf的數(shù)據(jù)結(jié)構(gòu)如圖:

當(dāng)用戶在log buffer的start_lsn-end_lsn間寫下redo log時,會標(biāo)記Link_buf相應(yīng)的位置,即將m_link[start_lsn%m_capacity]賦值為為end_lsn-start_lsn。
redo log記錄到log buffer的過程如下:
1.首先,各用戶線程寫redo log時,先根據(jù)redo log長度,向系統(tǒng)全局原子變量log_sys.sn獲取本次redo log日志的start_lsn, end_lsn。原子變量sn能保證各線程獲得的start_lsn-end_lsn區(qū)間連續(xù)無空洞;

2.用戶線程申請到start_lsn-end_lsn區(qū)間后,需要先等待到Link_buf推進(jìn)到自己可以使用的位置。

如圖所示,start_lsn0-end_lsn0,start_lsn2-end_lsn2, start_lsn3-end_lsn3為三個用戶線程新申請的lsn區(qū)間;start_lsn1-end_lsn1對應(yīng)的區(qū)間已經(jīng)標(biāo)記到link_buf上;start_lsn3-end_lsn3距離tail太遠(yuǎn),需要等待link_buf推進(jìn)才能使用;
3.寫入log buffer后,再將start_lsn->end_lsn的范圍標(biāo)記到link_buf(注意:因為只在start_lsn%capacity的位置標(biāo)記link_buf,所以即使end_lsn超過(m_tail, m_tail+m_capacity)也不影響);

4.用戶線程提交事務(wù)時設(shè)置事件log_sys.writer_event,觸發(fā)log_writer線程將日志從redo log buffer寫到系統(tǒng)緩存(log_writer線程自己也會輪詢link_buf判斷是否寫入了新的日志);
5.log_writer線程推進(jìn)m_tail,并將m_tail前的log buffer落盤。

redo log 的落盤及通知
前面簡述了redo log是如何提交的,在redo log提交以及落盤時,涉及多個線程,他們的關(guān)系如下:

用戶線程在讀寫事務(wù)提交時,會產(chǎn)生一些redo log,并隨著mtr提交而記錄到redo log buffer中,隨后用戶線程嘗試設(shè)置writer_event觸發(fā)log_writer線程寫日志,并監(jiān)聽屬于自己的flush_events[i]事件;
log_writer線程推進(jìn)Link_buf.m_tail,將最大連續(xù)lsn前的redo log寫入系統(tǒng)緩存,并設(shè)置flusher_event觸發(fā)log_flusher線程;
log_flusher線程將已寫入系統(tǒng)緩存的日志刷盤,并設(shè)置flush_notifier_event觸發(fā)log_flush_notifier線程通知用戶;
log_flush_notifier根據(jù)已刷盤的lsn換算出需要觸發(fā)的事件,通知用戶線程。
具體實現(xiàn)時,通過log_sys中的幾個成員變量,跟進(jìn)redo log的寫入情況。其中l(wèi)og_sys.recent_writtern.m_tail表示log buffer最大連續(xù)范圍;log_sys.write_lsn表示寫入到系統(tǒng)緩存的位置;log_sys.flushed_to_disk_lsn表示已落盤的位置。各標(biāo)記的推進(jìn)過程如下:

通知用戶線程
用戶提交事務(wù)時,會根據(jù)innodb_flush_log_at_trx_commit參數(shù),調(diào)用log_wait_for_write或log_wait_for_flush,來等待redo log寫入到系統(tǒng)緩存或刷到硬盤。用戶線程的通知是通過log_sys.flush_events事件數(shù)組來實現(xiàn)的,為了避免一次通知的flush_events過多,flush_events會像桶一樣劃分給不同的用戶線程:redo log是以一個個log block劃分的,假設(shè)log_sys.flush_events數(shù)組長度為m,則第n個log block的刷盤,由flush_events[n%m]事件監(jiān)聽。當(dāng)log buffer的第L1個log block到第L2個log block被刷盤時,會設(shè)置L1-L2之間的log block所屬的flush_events,從而redo log在L1-L2之間的用戶線程都會收到通知。

總結(jié)
mysql8.0通過redo log無鎖化,解決了用戶線程寫redo log時競爭鎖帶來的性能影響。同時將redo log寫文件、redo log刷盤從用戶線程中剝離出來,抽成單獨的線程,用戶線程只負(fù)責(zé)將redo log寫入到log buffer,不再關(guān)心redo log的落盤細(xì)節(jié),只需等待log_writer線程或log_flusher線程的通知。
以上就是MySQL 8.0 redo log的深入解析的詳細(xì)內(nèi)容,更多關(guān)于MySQL 8.0 redo log的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
MySQL定時備份數(shù)據(jù)庫(全庫備份)的實現(xiàn)
本文主要介紹了MySQL定時備份數(shù)據(jù)庫(全庫備份)的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
MySQL數(shù)據(jù)庫之存儲過程?procedure
這篇文章主要介紹了MySQL數(shù)據(jù)庫之存儲過程?procedure,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-06-06
MySQL學(xué)習(xí)之基礎(chǔ)命令實操總結(jié)
MySQL 是最流行的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),在WEB應(yīng)用方面MySQL是最好的。本文將為大家詳細(xì)介紹一些MySQL的基礎(chǔ)命令,需要的可以參考一下2022-03-03
MySQL優(yōu)化之SQL調(diào)優(yōu)策略分享
這篇文章主要介紹了MySQL優(yōu)化之SQL調(diào)優(yōu)策略,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04
MySQL錯誤提示:sql_mode=only_full_group_by完美解決方案
有時候遇到數(shù)據(jù)庫重復(fù)數(shù)據(jù),需要將數(shù)據(jù)進(jìn)行分組,并取出其中一條來展示,這時就需要用到group by語句,下面這篇文章主要給大家介紹了關(guān)于MySQL錯誤提示:sql_mode=only_full_group_by的完美解決方案,需要的朋友可以參考下2022-10-10
MYSQL row_number()與over()函數(shù)用法詳解
這篇文章主要介紹了MYSQL row_number()與over()函數(shù)用法詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
mysql函數(shù)拼接查詢concat函數(shù)的使用方法
下面小編就為大家?guī)硪黄猰ysql函數(shù)拼接查詢concat函數(shù)的使用方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
用MySQL創(chuàng)建數(shù)據(jù)庫和數(shù)據(jù)庫表代碼
了解了一些最基本的操作命令后,我們再來學(xué)習(xí)如何創(chuàng)建一個數(shù)據(jù)庫和數(shù)據(jù)庫表。2008-10-10

