精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Redo 日志從產(chǎn)生到寫入日志文件

數(shù)據(jù)庫 MySQL
MySQL 8.0 以前,Redo 日志是串行寫入 log buffer 的,多個用戶線程想要同時往 log buffer 里寫日志,那是不行的,必須排隊等待(獲取互斥鎖),拿到互斥鎖之后,才能往 log buffer 里寫日志。

對于這樣的劇情,想必大家不會陌生:美國大片中拯救世界的英雄,平時看起來跟普通人沒啥區(qū)別,甚至還可能會有點讓人看不上。

但是,關(guān)鍵時刻,卻能爆發(fā)出驚人能量,挽狂瀾于既倒,扶大廈于將傾,拯救世界于危難之中。

今天我們要聊的主角:Redo 日志,也是這樣的平民英雄。

本來 InnoDB 接收到插入、修改、刪除這樣的 DML 語句,以及創(chuàng)建表 & 索引、修改表結(jié)構(gòu)這樣的 DDL 語句,修改 Buffer Pool 中的數(shù)據(jù)頁之后就完事了。

因為要保證數(shù)據(jù)不丟失,事情就變的復(fù)雜了,修改了數(shù)據(jù)頁不算完,還要生成 Redo 日志,生成了也不算完,還要把它寫入 Redo 日志文件。

為了方便描述,本文后面會把 Redo 日志文件簡稱為日志文件。

通過以上描述,相信大家能夠發(fā)現(xiàn),生成 Redo 日志并寫入日志文件,顯然是額外操作,會額外消耗資源。

不惜額外消耗寶貴的服務(wù)器資源都要保存下來的東西,肯定不能是個繡花枕頭,那這個有用的枕頭什么時候能派上用場呢?

當(dāng)然是服務(wù)器累了想小憩一下(突然崩潰)的時候了 ^_^。

服務(wù)器也不容易,誰還沒有個突然崩潰的時候呢?

說了這么多,是時候確定 Redo 日志的歷史地位了:Redo 日志,在太平日子里,不但是個雞肋,更是個累贅,但是,別把它不當(dāng)英雄,關(guān)鍵時刻還得靠它拯救數(shù)據(jù)庫。

飯前甜點到此為止,接下來是正餐。

本文內(nèi)容基于 MySQL 8.0.29 源碼。

正文

1、概述

MySQL 8.0 以前,Redo 日志是串行寫入 log buffer 的,多個用戶線程想要同時往 log buffer 里寫日志,那是不行的,必須排隊等待(獲取互斥鎖),拿到互斥鎖之后,才能往 log buffer 里寫日志。

MySQL 8.0 中,串行寫入變?yōu)椴⑿袑懭?,log buffer 由鄉(xiāng)間小道變成了單向 8 車道的高速公路,多個用戶線程可以同時往 log buffer 里寫入 Redo 日志,效率大大提升。

Redo 日志從產(chǎn)生到刷盤,一共會經(jīng)歷 4 個階段(產(chǎn)生、寫 log buffer、寫日志文件、刷盤),本文會用 4 個小節(jié)分別介紹這 4 個階段。

2、Redo 日志產(chǎn)生

以一條非常簡單的插入語句為例,這個語句包含自增列,并且只插入一條記錄,我們假設(shè)插入過程中不會造成索引頁分裂,也不會產(chǎn)生溢出頁。

不考慮 Undo 日志產(chǎn)生的 Redo 日志,這樣一條 SQL 語句會包含 2 條 Redo 日志(這 2 條日志會形成一個日志組):

  • 一條日志中保存著表中自增列的最大值(MySQL 8.0 把自增列的值持久化了)。
  • 另一條日志中保存著插入記錄各字段的值。

每條日志中還有可能會包含 InnoDB 需要的其它信息。

插入記錄的過程中,會先產(chǎn)生一條 Redo 日志用于記錄表中自增列的最大值,然后插入記錄,再產(chǎn)生另一條 Redo 日志。

Redo 日志并不會每產(chǎn)生一條就馬上寫入 log buffer,而是一組 Redo 日志攢到一起往 log buffer 里寫。

問題來了,產(chǎn)生了一條 Redo 日志不能馬上寫入 log buffer,那怎么辦?

那就需要有一個地方臨時存放日志組中不同時間點產(chǎn)生的日志了,這個地方就是 mtr 中的 m_log 鏈表。

圖片圖片

m_log 鏈表是由一個一個 block 組成的鏈表,block 大小為 512 字節(jié),每產(chǎn)生一條日志,就追加到 m_log 的 block 中,如果一個 block 寫滿了,就再申請一個 block 接著寫。

那 mtr 又是個啥?

mtr 是 Mini-Transaction 的縮寫,是一組不可分隔的操作組成的一個整體,就像前面插入語句的例子中,保存表中自增列的最大值和插入記錄就是一組不可分隔的操作,必須放入一個 mtr。

兩個操作放入一個 mtr,它們的日志也就放在同一個 mtr 中了。這樣就能保證兩個操作產(chǎn)生的 Redo 日志一起寫入 log buffer 和日志文件中。

mtr 的用途可不止打包一組 Redo 日志這么簡單,它還會對 SQL 執(zhí)行過程中 mtr 需要訪問的 Buffer Pool 中的頁加鎖、修改頁中的數(shù)據(jù)、釋放鎖,本文我們只介紹 Redo 日志,對于 mtr 就不再展開了。

還有一個概念需要解釋一下,日志組就是一個 mtr 中的所有日志。

3、寫入 log buffer

mtr 中一組不可分隔的操作都完成之后,就該提交了,mtr 提交過程中要干的第一件事就是把它里面臨時存放的一組 Redo 日志寫入到 log buffer 中。

一個事務(wù)中可能會包含多個 mtr,mtr 的提交和事務(wù)的提交不是一個概念,不要混淆。

前面說到在 MySQL 8.0 中,往 log buffer 里寫日志不需要排隊等待(獲取互斥鎖),多個用戶線程可以同時寫入。

這個無鎖化設(shè)計是通過在 log buffer 中為每個 mtr 中的 Redo 日志預(yù)留空間實現(xiàn)的,每個 mtr 都有一段屬于自己的空間,各自往自己專屬的空間內(nèi)寫入日志,相互之間就不影響了。

用戶線程的 mtr 往 log buffer 寫 Redo 日志前,會先獲取一段序列號。

以當(dāng)前系統(tǒng)中已產(chǎn)生的最大序列號(SN)作為 start_sn,加上本次要往 log buffer 中寫入的 Redo 日志的字節(jié)數(shù)(len),得到 end_sn(end_sn = start_sn + len)。

start_sn ~ end_sn 就是本次要寫入 log buffer 的 Redo 日志的序列號區(qū)間。

獲取 start_sn、end_sn 的過程是原子操作,多個線程之間不會出現(xiàn)沖突,不會獲取到有交叉的序列號區(qū)間。

拿到 start_sn ~ end_sn 只是第一步,還需要進行一次轉(zhuǎn)換,把序列號(SN)轉(zhuǎn)換為日志序列號(LSN),得到一個 LSN 的范圍:start_lsn ~ end_lsn,這個范圍對應(yīng)著 log_buffer 中為 mtr 即將寫入的 Redo 日志預(yù)留的空間。

SN 是截止某個時刻,InnoDB 中實際產(chǎn)生的 Redo 日志字節(jié)數(shù)。

SN 按照 496 字節(jié)拆分,拆分后每 496 字節(jié),加上 12 字節(jié)的頭信息、4 字節(jié)尾部檢驗碼,得到 512 字節(jié)的 block,經(jīng)過這樣的轉(zhuǎn)換之后,得到的數(shù)字就是 LSN。

圖片圖片

至此,寫入日志到 log buffer 的準(zhǔn)備工作又往前推進了一步。

但是,別著急,也許還要再等等,如果 log buffer 中剩余空間不夠?qū)懭氘?dāng)前 mtr 的 Redo 日志,那就需要等到 log buffer 中的 Redo 日志被寫入日志文件,為當(dāng)前 mtr 的 Redo 日志騰出空間才行。

這里的寫入日志文件,只是調(diào)用了操作系統(tǒng)的寫文件方法,把 Redo 日志寫入日志文件的操作系統(tǒng)緩沖區(qū)中,日志文件暫時還不會刷新到磁盤上。

那怎么判斷 log buffer 中是否有空間呢?

要回答這個問題,我們需要先介紹一個屬性 log_sys.write_lsn,表示 LSN 小于 log_sys.writen_lsn 的日志都已經(jīng)寫入到日志文件緩沖區(qū)中。

end_sn <= log_sys.write_lsn + innodb_log_buffer_size(默認 16M),就表示 log buffer 中有空間寫入當(dāng)前 mtr 的 Redo 日志。

如果要等,總不能一直等吧,等到什么時候是個頭呢?

如果需要等待,用戶線程會監(jiān)聽 log.write_events 事件,log buffer 中有空間寫入 Redo 日志之后,當(dāng)前用戶線程會收到事件通知。

誰會給這些等待的用戶線程發(fā)送事件通知呢?后面會有介紹,請繼續(xù)往下看。

等到 log buffer 中有空間之后,往里面寫入日志就很簡單了,直接把 mtr 中的 Redo 日志拷貝到 log buffer 中就完事了。

寫完之后,還需要根據(jù) mtr 的 start_lsn 在 recent_written.m_links 中找到對應(yīng)的 SLOT,然后把 mtr 的 end_lsn 寫入這個 SLOT,表示這個 mtr 已經(jīng)把它的全部 Redo 日志寫入 log buffer 了。

如果根據(jù) start_lsn 在 recent_written.m_links 中找到的 SLOT 正在被其它 mtr 使用,當(dāng)前這個用戶線程會采用循環(huán) + 間隔休眠 20 毫秒的方式,直到 SLOT 可以使用。

前面兩段涉及到 recent_written 的介紹,大家看了可能會覺得一頭霧水,先不要著急,有個模糊印象就行。

因為這兩段邏輯是在寫日志到 log buffer 這個階段發(fā)生的,所以這里必須要提一下露個臉,相當(dāng)于占個位,但是詳細介紹放到 4. 寫入日志文件小節(jié)更合適。

說完了寫入 Redo 日志到 log buffer,我們回到用戶線程??等待?? log buffer 中有空間寫入它的 Redo 日志,這個等待過程是個躺平的過程,在這個過程中,用戶線程除了等待事件通知,其它事情啥也不干。

在用戶線程看來,等待的過程中歲月靜好,但是,世上本來沒有歲月靜好,它感受到的歲月靜好,無非是因為有人替它負重前行。

誰在負重前行?

那是另一個默默工作的線程,它的名字叫作 log_writer,它是一個搬運工,一個專門把 log buffer 中的 Redo 日志寫入到日志文件的線程。

log_writer 線程只調(diào)用操作系統(tǒng)寫文件方法,把 Redo 日志寫入日志文件,不會刷新到磁盤上,此時,Redo 日志還在日志文件的操作系統(tǒng)緩沖區(qū)中。

接下來,就是 log_writer 線程的主場了。

4、寫入日志文件

log writer 線程把 log buffer 中的 Redo 日志寫入日志文件緩沖區(qū),寫入的這一段 Redo 日志必須是連續(xù)的,中間不能出現(xiàn)空洞。

圖片圖片

上一個步驟中,不同用戶線程可以并行把各自 mtr 中的 Redo 日志寫入 log buffer 中,解決了寫入速度慢的問題,同時也帶來了新問題。

不同用戶線程的 Redo 日志量可能不一樣,有的線程會先寫完,有的線程后寫完,如果某一個范圍內(nèi),頭部的日志寫完了,尾部的日志也寫完了,中間的日志還沒寫完,這就出現(xiàn)了空洞。

舉個例子,假設(shè)有 3 個不同的用戶線程,各有一個 mtr 要提交,我們把這 3 個用戶線程的 mtr 分別叫作 mtr 10、mtr 11、mtr 12。

mtr 10 的 Redo 日志占用 200 字節(jié),LSN 范圍是 start_lsn(2097252) ~ end_lsn(2097452)。

mtr 11 的 Redo 日志占用 12045 字節(jié),LSN 范圍是 start_lsn(2097452) ~ end_lsn(2109497)。

mtr 12 的 Redo 日志占用 300 字節(jié),LSN 范圍是 start_lsn(2109497) ~ end_lsn(2109797)。

每一個 mtr 的 end_lsn 其實是不屬于它的,而是屬于下一個 mtr,是下一個 mtr 的 start_lsn。所以,每個 mtr 的 LSN 范圍是一個左閉右開區(qū)間,例如:mtr 10 [2097252, 2097452)。

mtr 10、mtr 12 的日志比較小,mtr 11 的日志比較大,可能會存在這樣的情況,mtr 10、mtr 12 的日志都已經(jīng)全部寫入 log buffer,mtr 11 的日志只有一部分寫入了 log buffer,中間是存在空洞的。

圖片

因為存在空洞,log_writer 線程不能把 mtr 10 ~ 12 的 Redo 日志都寫入日志文件,只能把 mtr 10 的 Redo 日志寫入日志文件。

等到 mtr 11 的 Redo 日志全部寫入 log buffer 之后,才能把 mtr 11 ~ 12 的 Redo 日志一起寫入日志文件。

那它怎么知道截止到哪個位置的日志是連續(xù)的,可以寫入日志文件的呢?

也許我們都能很快想到用一個變量把這個位置記錄下來就好了。

沒錯,InnoDB 也是這么干的,全局日志對象(log_sys)中,有一個 recent_written 屬性,這個屬性也是個對象,它有一個屬性 m_tail(log_sys.recent_written.m_tail),用來記錄 log buffer 中小于哪個 LSN 的日志都是連續(xù)的。

知道了用什么記,現(xiàn)在有個關(guān)鍵問題,那就是怎么記?

recent_written 對象,有個屬性 m_links(recent_written.m_links),這是個數(shù)組,默認有 1048576 個元素,每個元素是一個 SLOT,每個 SLOT 占用 8 字節(jié),總共占用 8M 內(nèi)存空間。

圖片

m_links 的每個 SLOT 對應(yīng) log buffer 中的一個 LSN,每個用戶線程的 mtr 往 log buffer 中寫入它的全部 Redo 日志之后,會根據(jù) start_lsn 在 m_links 中找到一個 SLOT,并把 end_lsn 寫入這個 SLOT。

還是以前面的 mtr 10 ~ 12 為例,當(dāng) mtr 10 把它的所有 Redo 日志全部寫入 log buffer 之后,根據(jù) start_lsn(2097252) 找到對應(yīng)的 SLOT 并寫入 end_lsn(2097452)。

SLOT 下標(biāo) = start_lsn(2097252) % SLOT 數(shù)量(1048576) = 100。

m_links[100] = end_lsn(2097452),m_links[101 ~ 299] 對應(yīng)著 LSN 2097253 ~ 2097451,也屬于 mtr 10 的范圍,不過這個區(qū)間只是用來占位的,mtr 10 并不會往其中的 SLOT 寫入 LSN。

圖片圖片

重要說明:實際上,因為 m_links 被當(dāng)作環(huán)形結(jié)構(gòu)循環(huán)、重復(fù)使用,每個 SLOT 都有可能曾經(jīng)被其它 mtr 寫入過 end_lsn。

對于 mtr 10 來說,除了 start_lsn 對應(yīng)的 SLOT(m_links[100])的值是 end_lsn(2097452) 之外,其它 SLOT(m_links[101 ~ 299])的值可能是 0,也可能是之前的某個 mtr 寫入的 end_lsn。

如果 SLOT 的值是之前的某個 mtr 寫入的 end_lsn,這個 end_lsn 一定是小于等于 mtr 10 的 start_lsn 的。

當(dāng) mtr 12 把它的所有 Redo 日志全部寫入 log buffer 之后,根據(jù) start_lsn(2109497) 找到對應(yīng)的 SLOT 并寫入 end_lsn(2109797)。

SLOT 下標(biāo) = start_lsn(2109497) % SLOT 數(shù)量(1048576) = 12345。

m_links[12345] = end_lsn(2109797),m_links[12346 ~ 12644] 對應(yīng)著 LSN 2109498 ~ 2109796,也屬于 mtr 12 的范圍,這個區(qū)間內(nèi) SLOT 的值可能為 0 或者小于等于 start_lsn(2109497) 的數(shù)字(具體原因可以參照 mtr 10 的說明)。

圖片圖片

此時,mtr 11 的 Redo 日志還沒有全部寫入 log buffer,m_links[300 ~ 12344] 對應(yīng)著 LSN 2097452 ~ 2109496,屬于 mtr 11 的范圍,這個區(qū)間內(nèi) SLOT 的值可能為 0 或小于等于 start_lsn(2097452) 的數(shù)字(具體原因可以參照 mtr 10 的說明)。

圖片圖片

說完了 mtr 10 ~ 12 的狀態(tài),接下來就要正式介紹 Redo 日志寫入日志文件的關(guān)鍵步驟了:根據(jù) recent_written.m_links 找到 log buffer 中連續(xù)的日志區(qū)間。

先來回憶一下:

  • recent_written.m_tail?,表示 log buffer 中小于 recent_written.m_tail 的日志都是連續(xù)的。
  • log_sys.write_lsn?, 表示 log buffer 中小于 log_sys.write_lsn 的日志都已經(jīng)寫入日志文件了。

假設(shè),此時 recent_written.m_tail = 2097252,這是 mtr 10 的 start_lsn,表示 mtr 10 之前的 mtr 往 log buffer 中寫入的 Redo 日志已經(jīng)是連續(xù)的了。

圖片圖片

log_writer 線程接下來從 m_tail 對應(yīng)的 LSN(2097252)開始,尋找更大范圍的連續(xù)日志區(qū)間。

計算 m_tail 對應(yīng)的 SLOT 下標(biāo) = m_tail(2097252) % SLOT 數(shù)量(1048576) = 100。

讀取 SLOT 100(下標(biāo)為 100 的 SLOT)的值,得到 2097452,這是 mtr 10 的 end_lsn,也是 mtr 11 的 start_lsn,說明 mtr 10 的日志已寫入 log buffer。

LSN < 2097452 的區(qū)間,Redo 日志都是連續(xù)的了,更新 m_tail 的值,recent_written.m_tail = 2097452。

圖片

繼續(xù)尋找,計算 m_tail 對應(yīng)的 SLOT 下標(biāo) = m_tail(2097452) % SLOT 數(shù)量(1048576) = 300。

讀取 SLOT 300 的值,得到 0,說明 mtr 11 還沒有把 Redo 日志全部寫入 log buffer 了,本次尋找更大范圍的連續(xù)日志區(qū)間結(jié)束,m_tail 保持為 2097452 不變。

圖片

log_writer 線程可以把 log buffer 中 LSN < m_tail(2097452) 的 Redo 日志寫入到日志文件,寫完之后,更新 log_sys.write_lsn 的值,log_sys.write_lsn = 2097452。

圖片

然后,log_writer 線程或 log_write_notifier 線程會通知正在等待往 log buffer 中 LSN < m_tail(2097452) 這個區(qū)間寫 Redo 日志的用戶線程,告訴它們可以寫 Redo 日志了。

為了減輕 log_writer 線程的負擔(dān),通知用戶線程這個邏輯做了區(qū)分:

如果只有一個用戶線程正在等待往 log buffer 中 LSN < m_tail(2097452) 區(qū)間寫 Redo 日志,log_writer 線程順手就通知這個用戶線程了。

如果有多個用戶線程正在等待往  log buffer 中 LSN < m_tail(2097452) 區(qū)間寫 Redo 日志,log_writer 線程會讓 log_write_notifier 線程去通知等待這個范圍可寫的所有用戶線程。

3. 寫入 log buffer 小節(jié)說過,如果用戶線程需要等待 log buffer 中有空間寫入它的 Redo 日志,這個用戶線程會監(jiān)聽 log.write_events 事件,log_writer & log_write_notifier 線程就是通過這個事件通知用戶線程的。

實際上,用戶線程監(jiān)聽的是 log.write_events[slot],slot 是對 mtr 的 start_lsn 取模計算得到的,計算公式是這樣的:slot = start_lsn % recent_written.m_links 的 SLOT 數(shù)量(默認 1048576)。

監(jiān)聽到具體的 slot 上是為了保證每個用戶線程只會接收到 log.write_events 事件中和自己有關(guān)的通知。

過了一小會,log_writer 線程又要開始工作了,此時,mtr 11 中的全部 Redo 日志都寫入 log buffer 了。

上次結(jié)束時,recent_written.m_tail = 2097452,其對應(yīng)的 SLOT 下標(biāo)為 300,這次從 SLOT 300 開始繼續(xù)尋找。

讀取 SLOT 300 的值,得到 2109497,這是 mtr 11 的 end_lsn,也是 mtr 12 的 start_lsn,說明 LSN < 2109497 的區(qū)間,Redo 日志都是連續(xù)的了,更新 m_tail 的值,recent_written.m_tail = 2109497。

圖片圖片

繼續(xù)尋找,計算 m_tail 對應(yīng)的 SLOT 下標(biāo) = m_tail(2109497) % SLOT 數(shù)量(1048576) = 12345。

讀取 SLOT 12345 的值,得到 2109797,這是 mtr 12 的 end_lsn,也是 mtr 12 之后的下一個 mtr 的 start_lsn,說明 LSN < 2109797 的區(qū)間,Redo 日志都是連續(xù)的了,更新 m_tail 的值, recent_written.m_tail = 2109797。

圖片圖片

繼續(xù)尋找,計算 m_tail 對應(yīng)的 SLOT 下標(biāo) = m_tail(2109797) % SLOT 數(shù)量(1048576) = 12645。

讀取 SLOT 12645 的值,得到 0,說明 Redo 日志連續(xù)的區(qū)間到這里暫時結(jié)束,m_tail 保持為 2109797 不變。

log_writer 線程可以把 log buffer 中 LSN < m_tail(2109797) 的 Redo 日志寫入到日志文件了,寫完之后,更新 log_sys.write_lsn 的值,log_sys.write_lsn = 2109797。

圖片圖片

然后,log_writer 線程或 log_write_notifier 線程會觸發(fā) log.write_events 事件,通知正在等待往 LSN < m_tail(2109797) 區(qū)間內(nèi)寫 Redo 日志的用戶線程,告訴它們可以寫 Redo 日志了。

5、日志文件刷盤

Redo 日志從 log buffer 寫入日志文件中,并不是直接就寫到磁盤文件中了,而是會先進入日志文件在操作系統(tǒng)的緩沖區(qū)中,還需要經(jīng)過刷盤操作才能最終寫到磁盤上的日志文件中,成為持久化的日志。

Redo 日志文件刷盤,也是由專門的線程完成的,這個線程是 log_flusher。

log_flusher 線程的常規(guī)工作是大概每秒執(zhí)行一次刷盤操作。

全局日志對象(log_sys)中有一個屬性 flushed_to_disk_lsn 表示小于 log_sys.flushed_to_disk_lsn 的 Redo 日志都已經(jīng)刷新到磁盤上的日志文件中了。

前面我們還提到了另一個屬性 log_sys.write_lsn,表示 log buffer 中小于 log_sys.write_lsn 的日志都已經(jīng)寫入日志文件了。

每次執(zhí)行刷盤操作時,對比這兩個屬性的值,就能判斷出來日志文件緩沖區(qū)中是不是有新的 Redo 日志需要刷盤。

如果 log_sys.write_lsn 大于 log_sys.flushed_to_disk_lsn,說明需要刷盤,否則本次不需要執(zhí)行刷盤操作,log_flusher 線程可以愉快的躺平大概 1s 左右,然后等待下一次時間到了,再進行同樣的邏輯判斷,確定是否需要刷盤。

圖片

不出意外的話,log_flusher 線程就是這么簡單平凡,日復(fù)一日,年復(fù)一年的機械單調(diào)的工作著。

但是,這顯然不符合劇情發(fā)展,單調(diào)的故事中總是會時不時出現(xiàn)點刺激的劇情。

log_flusher 線程除了常規(guī)的每秒執(zhí)行一次刷盤操作,還會監(jiān)聽一個事件:log.flusher_event,通過這個事件和外界保持聯(lián)系,接受外部刺激。

我們來看一個帶給 log_flusher 線程刺激場景:

innodb_flush_log_at_trx_commit = 1 時,事務(wù)每次提交的時候,都心急火燎的,不可能心平氣和的等著 log_flusher 每秒執(zhí)行一次刷盤操作,必須讓 log_flusher 立馬起來干活(事務(wù)會觸發(fā) log.flusher_event 事件),把事務(wù)中產(chǎn)生的 Redo 日志刷盤,然后,事務(wù)才能向客戶端交差。

innodb_flush_log_at_trx_commit = 2 時,事務(wù)心急火燎的對象就不是 log_flusher 線程了,而是 log_writer 線程,因為這種場景下,事務(wù)只需要等待 log_writer 線程把它的 Redo 日志寫入日志文件緩沖區(qū)就可以了,不需要等待刷盤。

事務(wù)催促 log_flusher 執(zhí)行刷盤操作之后,會等待刷盤操作完成。等待過程是通過監(jiān)聽 log.flush_events[slot] 事件實現(xiàn)的。

slot 是對事務(wù)中最后一個 mtr(一個事務(wù)可以包含多個 mtr)的 end_lsn 取模計算得到的,計算公式是這樣的:slot = end_lsn % recent_written.m_links 的 SLOT 數(shù)量(默認 1048576)。

slot 的作用是保證每個用戶線程只會接收到 log.flush_events 事件中和自己有關(guān)的通知。

刷盤操作完成后,log_flusher 線程或 log_flush_notifier 線程會通知正在等待 LSN < m_tail(2097452) 這個區(qū)間內(nèi)的 Redo 日志刷盤的用戶線程。

為了減輕 log_flusher 線程的負擔(dān),通知用戶線程這個邏輯做了區(qū)分:

如果只有一個用戶線程正在等待本次刷盤結(jié)果,log_flusher 線程順手就通知這個用戶線程了。

如果有多個用戶線程正在等待本次刷盤結(jié)果,log_flusher 線程會讓 log_flush_notifier 線程去通知等待本次刷盤結(jié)果的所有用戶線程。

6、總結(jié)

Redo 日志是以日志組為單位寫入 log buffer 和日志文件的,每個日志組的 Redo 日志都來源于一個 mtr。

多個用戶線程的 mtr 以無鎖的方式并行往 log buffer 里寫入 Redo 日志,只需要寫入之前計算出來 mtr 中 Redo 日志的 LSN 范圍,通過這個 LSN 范圍在 log buffer 中鎖定一段區(qū)間,多個用戶線程鎖定的區(qū)間不一樣,不會出現(xiàn)沖突。

log_writer 線程把已經(jīng)寫入 log buffer 的 Redo 日志寫入日志文件,需要保證 Redo 日志是連續(xù)的,InnoDB 用 log_sys.recent_written 對象中的 m_links 數(shù)組、m_tail 屬性來輔助 log_writer 線程找到連續(xù)的日志區(qū)間。

log_writer 線程把 log buffer 中的 Redo 日志寫入日志文件之后,會通知等待 log buffer 為它騰出空間的用戶線程,或者讓 log_write_notifier 線程通知用戶線程。

log_flusher 線程每秒執(zhí)行一次刷盤操作,同時還監(jiān)聽了 log.flusher_event 事件,用于接收外部刺激,觸發(fā)它在周期性刷盤工作的時候也能夠更及時的刷盤。

如果 log_sys.write_lsn 大于 log_sys.flushed_to_disk_lsn 說明需要執(zhí)行刷盤操作,否則不需要。

log_flusher 線程執(zhí)行完刷盤操作之后,也會通知等待刷盤操作完成的用戶線程,或者讓 log_flush_notifier 線程通知用戶線程。

最后,放上一張整體流程圖,希望能夠有助于大家理解 Redo 日志刷盤的整體流程。

圖片

本文轉(zhuǎn)載自微信公眾號「一樹一溪」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系一樹一溪公眾號。

責(zé)任編輯:姜華 來源: 一樹一溪
相關(guān)推薦

2021-01-06 08:41:03

SQL Redo日志

2024-04-22 08:10:29

2023-05-09 08:34:51

PythonWith語句

2021-06-02 06:02:50

Loki 源碼分析日志

2010-10-29 15:07:33

oracle日志

2023-10-09 14:36:28

工具PLGEFK

2022-08-15 09:00:23

數(shù)據(jù)庫日志

2010-09-13 14:12:21

SQL Server日

2010-03-09 17:57:46

2025-02-17 15:06:07

2025-06-03 08:35:00

命令Linux日志分析

2025-03-31 08:20:00

SQL 查詢數(shù)據(jù)庫dsq

2024-12-18 18:53:48

2009-11-16 17:33:21

重做Oracle日志文

2010-11-19 13:01:31

Oracle日志文件

2010-11-26 16:37:41

MySQL日志文件

2010-10-29 14:29:55

Oracle移動重做日

2011-04-06 15:25:20

Delphi

2023-09-11 11:31:53

VLC日志文件

2020-08-20 12:10:42

MySQL日志數(shù)據(jù)庫
點贊
收藏

51CTO技術(shù)棧公眾號

美女免费久久| 男人天堂av在线播放| 麻豆视频久久| 午夜精品久久久久久久久| 久久99热只有频精品91密拍| 久久国产乱子伦精品| 亚洲电影影音先锋| 精品网站999www| 五月六月丁香婷婷| 亚洲黄色网址| 国产精品看片你懂得| 高清不卡一区二区三区| 性高潮视频在线观看| 欧美日韩一区二区国产| 国产亚洲美女久久| 中文字幕99页| 国产精品国产亚洲精品| 色八戒一区二区三区| 超级碰在线观看| 黄色在线观看网| 懂色一区二区三区免费观看| 国产精品美女久久久久av超清| 9191在线视频| 日本综合视频| 亚洲高清不卡在线观看| 最新不卡av| 风间由美一区| 91丨porny丨国产| 成人动漫视频在线观看免费| 影音先锋国产资源| 免费在线欧美黄色| 午夜免费日韩视频| 欧美毛片在线观看| 亚洲精品网址| 日韩在线免费观看视频| 熟女俱乐部一区二区| 久久365资源| 日韩精品专区在线| www.国产福利| 欧美天堂一区二区| 欧美性xxxxxxxx| 国产又大又硬又粗| 蜜桃av在线播放| 亚洲成av人片一区二区三区| 久久久久久久香蕉| 三级福利片在线观看| 亚洲欧美另类久久久精品2019| 99国产视频在线| 国产乱码久久久| 久久99精品久久久久久国产越南| 久久成人国产精品| 午夜国产小视频| 9999国产精品| 日韩一区二区欧美| 最新日韩免费视频| 日韩一区电影| 日韩视频―中文字幕| 中文字幕在线观看二区| 欧美hd在线| 久久视频国产精品免费视频在线| 日韩aaaaa| 欧美jizz19性欧美| 亚洲美女av黄| 国产一级久久久久毛片精品| 欧美一区二区麻豆红桃视频| 中文字幕欧美亚洲| 多男操一女视频| 欧美日韩日本国产亚洲在线| 欧美日韩成人在线观看| 日本三级免费看| 久久动漫亚洲| 国产精品视频区| 在线观看国产成人| 国产精品一区二区久久精品爱涩 | 噜噜噜91成人网| 97在线视频免费播放| www.国产色| 欧美aaa在线| 91欧美精品午夜性色福利在线| 五月婷婷开心网| 丝袜a∨在线一区二区三区不卡| 色综合天天综合网国产成人网 | 亚洲精品中文字幕在线观看| 国产日韩欧美大片| 国产美女情趣调教h一区二区| 国产精品久久久久久久久动漫| 国产日韩三区| 成人高清在线| 一级特黄大欧美久久久| 国产主播自拍av| 日韩经典一区| 欧美成人激情免费网| 超碰97人人干| 91精品婷婷色在线观看| 欧美亚洲成人网| 在线观看国产一区二区三区| 国产精品1区2区3区在线观看| 成人免费福利在线| 成人午夜免费福利| 国产精品久久久久久久午夜片| 日本视频一区在线观看| 黄网址在线观看| 欧美性色视频在线| 四虎国产精品永久免费观看视频| **日韩最新| 成人性视频网站| 亚洲成人av中文| 男人日女人视频网站| 日韩精品免费观看视频| 欧美变态凌虐bdsm| 亚洲国产欧美视频| 欧美日韩亚洲一区三区 | eeuss一区二区三区| 天堂网www中文在线| 一区二区中文视频| 东北少妇不带套对白| 久久伊人国产| 国产视频在线一区二区| 欧美色图一区二区| 开心九九激情九九欧美日韩精美视频电影 | 国产美女福利视频| 国产精品美女久久久| 成人免费淫片aa视频免费| 欧美777四色影视在线 | 色综合久久av| 91九色国产在线播放| 欧美二区三区的天堂| 精品人妻无码一区| 国产精品久久久久久久久久妞妞| 日本a级片电影一区二区| 精品人妻午夜一区二区三区四区 | 视频在线在亚洲| 成人欧美一区二区三区视频| 精品美女在线观看视频在线观看| 亚洲国产另类av| 特黄特黄一级片| 久久亚洲国产| 国产精品丝袜高跟| 第九色区av在线| 一本色道久久综合亚洲精品按摩| 国产精品乱码久久久久| 色吊丝一区二区| 91成人国产在线观看| 人妻丰满熟妇av无码区hd| 亚洲精品中文在线| 国产精品欧美性爱| 欧美午夜影院| 国产精品美女诱惑| av剧情在线观看| 日韩电影大全免费观看2023年上| 一区二区三区在线观看免费视频| 久久久五月天| 亚洲一区二区三区视频播放| 高清全集视频免费在线| 777亚洲妇女| 女同久久另类69精品国产| 久久99九九99精品| 91精品一区二区三区四区| 在线视频亚洲欧美中文| 97视频在线观看成人| 免费国产在线观看| 欧美视频一区二区三区| 99久久久无码国产精品不卡| 国产成人综合亚洲网站| 国产午夜福利在线播放| 欧美禁忌电影网| 国产精品尤物福利片在线观看| 天堂av手机版| 色婷婷综合久久久中文一区二区 | 欧美黄色一级片视频| 精品国产一区二区三区| 国产精品久久久久久久久久东京| 天堂网在线观看视频| 一本大道久久a久久综合| 呻吟揉丰满对白91乃国产区| 国产一区 二区 三区一级| 久久这里只有精品8| 亚洲视频分类| 成人午夜激情网| av手机在线观看| 亚洲欧洲中文天堂| 91丨九色丨蝌蚪丨对白| 亚洲一区二区三区四区在线| 加勒比一区二区| 久久99久久99小草精品免视看| 色婷婷精品国产一区二区三区| 欧美gv在线观看| 色黄久久久久久| 亚洲成人一二三区| 日本久久精品电影| 青娱乐国产精品| 国产日韩欧美激情| 亚洲少妇一区二区三区| 日本va欧美va精品发布| 国产 欧美 日韩 一区| 亚洲色图丝袜| 成人动漫视频在线观看完整版| av在线播放观看| 亚洲美女av在线播放| 国产普通话bbwbbwbbw| 色婷婷久久久综合中文字幕| 国产又粗又硬又长又爽| 久久久夜色精品亚洲| 亚洲精品鲁一鲁一区二区三区| 欧美日韩精品一本二本三本| 日韩高清av| 国产精品黄网站| 成人午夜一级二级三级| 欧美不卡高清一区二区三区| 欧美激情手机在线视频| 在线免费看av| 亚洲男人天堂视频| 免费成人在线看| 91精品国产综合久久久久久久| 天天爽天天爽天天爽| 26uuu另类欧美| 自拍视频第一页| 激情欧美一区二区三区在线观看| 婷婷视频在线播放| 国内精品伊人久久久| 国产欧美日韩综合精品二区| 精品视频在线观看网站| 国产精品久久久久久五月尺| 伊人色综合一区二区三区影院视频| 亚洲视频在线观看视频| 午夜视频福利在线| 亚洲第一天堂无码专区| 精品人妻伦一二三区久久| 欧美日韩亚洲国产综合| 精品成人无码久久久久久| 大桥未久av一区二区三区| 国产精品suv一区二区| 一区二区三区国产精品| 51精品免费网站| 亚洲天堂福利av| 五月天免费网站| 国产精品久久久久久久久晋中| 亚洲成年人av| 成人精品在线视频观看| 91丨porny丨九色| 国产一区二区三区国产| 午夜xxxxx| 精品一区二区久久久| 做a视频在线观看| 久久成人精品无人区| 污污的视频免费观看| 韩国av一区二区三区| 亚洲免费在线播放视频| 国内精品免费**视频| 久久久九九九热| 国产精品99久久久久久久vr| 国内av免费观看| 国产成人av电影| 稀缺小u女呦精品呦| 本田岬高潮一区二区三区| 免费的av网站| 国产视频911| 蜜桃av免费观看| 亚洲美女一区二区三区| 久久久久99精品成人片毛片| 亚洲高清中文字幕| 探花视频在线观看| 欧美色区777第一页| 99久久免费国产精精品| 欧美一区二区三区男人的天堂| 久久久久亚洲视频| 欧美日韩在线三级| 国产高清视频免费观看| 亚洲第一区中文99精品| 猫咪在线永久网站| 日韩中文在线中文网三级| 中文av资源在线| 欧美一级淫片丝袜脚交| 日韩久久一区二区三区| 成人久久久久久久| 欧美高清视频看片在线观看| 色综合久久av| 精品动漫一区| 91最新在线观看| 丰满岳乱妇一区二区三区| 欧美成人午夜精品免费| ㊣最新国产の精品bt伙计久久| 人妻视频一区二区| **性色生活片久久毛片| 日韩三级一区二区三区| 欧美色图一区二区三区| 成人免费公开视频| 综合网日日天干夜夜久久| 色婷婷在线播放| 国产精品久久精品| 97青娱国产盛宴精品视频| 色乱码一区二区三在线看| 好看的日韩av电影| 在线观看的毛片| 不卡区在线中文字幕| 韩国一级黄色录像| 精品美女久久久久久免费| 亚洲一区在线观| 亚洲精品成a人在线观看| 免费日本一区二区三区视频| 91av福利视频| 久久av偷拍| 视频一区视频二区视频三区视频四区国产 | 国产精品一区二区在线观看网站| 911福利视频| 99久久综合99久久综合网站| 日韩精品一区二区亚洲av性色| 亚洲欧美一区二区三区国产精品| 久久爱一区二区| 色婷婷综合中文久久一本| 亚洲av永久无码国产精品久久 | 神马久久精品| 欧美理论片在线观看| 欧美影视资讯| 久久久久免费网| 国产一区欧美| 亚洲免费成人在线视频| 国产亚洲精品中文字幕| 人人干人人干人人干| 日韩欧美一区二区在线视频| 97人人在线| 国产精品美女久久久久av超清| 日本99精品| 亚洲一卡二卡三卡四卡无卡网站在线看 | 疯狂试爱三2浴室激情视频| 色天使久久综合网天天| 亚洲 欧美 激情 另类| 高清欧美性猛交| 91久久精品无嫩草影院| 蜜臀av.com| 国产毛片精品国产一区二区三区| 曰本三级日本三级日本三级| 中文字幕亚洲成人| 精品人妻无码一区二区性色| 亚洲精品久久久久久久久| 丁香花在线高清完整版视频| 91成人免费看| 欧美日韩亚洲一区二区三区在线| 久久国产色av免费观看| 91香蕉视频黄| 亚洲日本视频在线观看| 国产视频精品久久久| 色老头在线一区二区三区| 蜜桃视频成人| 日日夜夜一区二区| 一级片视频免费看| 欧美日韩一区二区不卡| 999国产在线视频| 国产女精品视频网站免费| 水蜜桃精品av一区二区| 午夜不卡福利视频| 亚洲精品欧美在线| 国产 欧美 自拍| 欧美在线观看网址综合| 中国av一区| 天天干天天综合| 亚洲欧美日韩久久| 空姐吹箫视频大全| 2019中文字幕在线观看| 欧美日韩一二三四| 亚洲免费黄色网| 亚洲综合久久av| 免费看男男www网站入口在线| 久久久久女教师免费一区| 麻豆精品少妇| 91淫黄看大片| 国产精品不卡在线| www.黄色小说.com| 538国产精品视频一区二区| 国产乱码精品一区二区亚洲 | 国产少妇在线观看| 日韩午夜激情免费电影| 国产精品蜜芽在线观看| 日本高清一区| 国产在线视频一区二区| 国产精品第56页| 国产亚洲欧美另类中文| 精品视频一区二区三区在线观看| 日韩欧美一区二区视频在线播放 | 午夜精品一区在线观看| 天天影院图片亚洲| 国产精品偷伦视频免费观看国产| 伊人久久综合影院| 性欧美在线视频| 亚洲v日本v欧美v久久精品| 国产在线免费观看| 91视频免费进入| 天堂蜜桃一区二区三区 | 欧美视频一二区| 青草青草久热精品视频在线观看| 欧美精品密入口播放| 男人的天堂最新网址| 精品久久久久久国产91| 888av在线| 久久伊人一区二区| 国产呦萝稀缺另类资源| 欧美brazzers| 国外成人在线直播| 中文字幕午夜精品一区二区三区| 亚洲自拍第三页|