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

Redo 日志如何在關(guān)鍵時(shí)刻拯救數(shù)據(jù)庫(kù)?

數(shù)據(jù)庫(kù) 新聞
Redo 日志,在太平日子里,不但是個(gè)雞肋,更是個(gè)累贅,但是,別把它不當(dāng)英雄,關(guān)鍵時(shí)刻還得靠它拯救數(shù)據(jù)庫(kù)。

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

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

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

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

因?yàn)橐WC數(shù)據(jù)不丟失,事情就變得復(fù)雜了,修改了數(shù)據(jù)頁(yè)不算完,還要生成 Redo 日志,生成了也不算完,還要把它寫入 Redo 日志文件。

為了方便描述,本文后面會(huì)把 Redo 日志文件簡(jiǎn)稱為日志文件。

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

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

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

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

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

飯前甜點(diǎn)到此為止,接下來是正餐。

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

一、概述

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

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

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

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

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

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

  • 一條日志中保存著表中自增列的最大值(MySQL 8.0 把自增列的值持久化了)。

  • 另一條日志中保存著插入記錄各字段的值。

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

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

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

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

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

圖片

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

那 mtr 又是個(gè)啥?

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

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

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

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

三、寫入 log buffer

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

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

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

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

用戶線程的 mtr 往 log buffer 寫 Redo 日志前,會(huì)先獲取一段序列號(hào)。

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

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

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

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

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

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

圖片

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

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

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

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

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

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

如果要等,總不能一直等吧,等到什么時(shí)候是個(gè)頭呢?

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

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

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

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

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

前面兩段涉及到 recent_written 的介紹,大家看了可能會(huì)覺得一頭霧水,先不要著急,有個(gè)模糊印象就行。因?yàn)檫@兩段邏輯是在寫日志到 log buffer 這個(gè)階段發(fā)生的,所以這里必須要提一下露個(gè)臉,相當(dāng)于占個(gè)位,詳細(xì)介紹在第四部分。

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

在用戶線程看來,等待的過程中歲月靜好,但是,世上本來沒有歲月靜好,它感受到的歲月靜好,無非是因?yàn)橛腥颂嫠?fù)重前行。

誰在負(fù)重前行?

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

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

接下來,就是 log_writer 線程的主場(chǎng)了。

四、寫入日志文件

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

圖片

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

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

舉個(gè)例子,假設(shè)有 3 個(gè)不同的用戶線程,各有一個(gè) mtr 要提交,我們把這 3 個(gè)用戶線程的 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)。

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

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

圖片

因?yàn)榇嬖诳斩矗琹og_writer 線程不能把 mtr 10 ~ 12 的 Redo 日志都寫入日志文件,只能把 mtr 10 的 Redo 日志寫入日志文件。

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

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

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

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

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

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

圖片

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

還是以前面的 mtr 10 ~ 12 為例,當(dāng) mtr 10 把它的所有 Redo 日志全部寫入 log buffer 之后,根據(jù) start_lsn(2097252) 找到對(duì)應(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] 對(duì)應(yīng)著 LSN 2097253 ~ 2097451,也屬于 mtr 10 的范圍,不過這個(gè)區(qū)間只是用來占位的,mtr 10 并不會(huì)往其中的 SLOT 寫入 LSN。

圖片

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

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

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

當(dāng) mtr 12 把它的所有 Redo 日志全部寫入 log buffer 之后,根據(jù) start_lsn(2109497) 找到對(duì)應(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] 對(duì)應(yīng)著 LSN 2109498 ~ 2109796,也屬于 mtr 12 的范圍,這個(gè)區(qū)間內(nèi) SLOT 的值可能為 0 或者小于等于 start_lsn(2109497) 的數(shù)字(具體原因可以參照 mtr 10 的說明)。

圖片

此時(shí),mtr 11 的 Redo 日志還沒有全部寫入 log buffer,m_links[300 ~ 12344] 對(duì)應(yīng)著 LSN 2097452 ~ 2109496,屬于 mtr 11 的范圍,這個(gè)區(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è),此時(shí) recent_written.m_tail = 2097252,這是 mtr 10 的 start_lsn,表示 mtr 10 之前的 mtr 往 log buffer 中寫入的 Redo 日志已經(jīng)是連續(xù)的了。

圖片

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

計(jì)算 m_tail 對(duì)應(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ù)尋找,計(jì)算 m_tail 對(duì)應(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 線程會(huì)通知正在等待往 log buffer 中 LSN < m_tail(2097452) 這個(gè)區(qū)間寫 Redo 日志的用戶線程,告訴它們可以寫 Redo 日志了。

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

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

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

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

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

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

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

上次結(jié)束時(shí),recent_written.m_tail = 2097452,其對(duì)應(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ù)尋找,計(jì)算 m_tail 對(duì)應(yīng)的 SLOT 下標(biāo) = m_tail(2109497) % SLOT 數(shù)量(1048576) = 12345。

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

圖片

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

讀取 SLOT 12645 的值,得到 0,說明 Redo 日志連續(xù)的區(qū)間到這里暫時(shí)結(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 線程會(huì)觸發(fā) log.write_events 事件,通知正在等待往 LSN < m_tail(2109797) 區(qū)間內(nèi)寫 Redo 日志的用戶線程,告訴它們可以寫 Redo 日志了。

五、日志文件刷盤

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

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

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

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

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

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

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

圖片

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

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

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

我們來看一個(gè)帶給 log_flusher 線程刺激場(chǎng)景:

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

innodb_flush_log_at_trx_commit = 2 時(shí),事務(wù)心急火燎的對(duì)象就不是 log_flusher 線程了,而是 log_writer 線程,因?yàn)檫@種場(chǎng)景下,事務(wù)只需要等待 log_writer 線程把它的 Redo 日志寫入日志文件緩沖區(qū)就可以了,不需要等待刷盤。

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

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

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

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

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

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

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

六、總結(jié)

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

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

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

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

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

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

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

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

圖片

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

2014-08-05 17:16:12

WiFi 華為

2023-12-26 01:19:50

Linux網(wǎng)絡(luò)診斷

2024-02-26 00:00:00

Linux技巧工具

2013-12-16 10:58:40

2020-02-12 16:45:00

黑客網(wǎng)絡(luò)安全Windows

2016-05-10 10:23:02

垂直Saas

2024-01-07 19:48:12

2011-05-03 11:03:47

噴墨打印機(jī)

2023-08-11 07:13:58

人工智能首席信息官平臺(tái)服務(wù)

2011-05-26 09:36:07

Oracle數(shù)據(jù)庫(kù)Redo故障

2011-03-24 17:21:42

Oracle數(shù)據(jù)庫(kù)Redo故障

2023-04-05 14:37:43

2020-03-24 10:13:43

網(wǎng)絡(luò)運(yùn)營(yíng)商疫情

2009-06-01 09:57:43

netbeans連接數(shù)netbeans數(shù)據(jù)庫(kù)netbeans連接m

2009-07-07 14:56:33

JSP連接MySQL

2019-08-19 14:06:27

MySQLDocker數(shù)據(jù)庫(kù)

2011-07-26 14:34:28

openSUSEpostgresql

2025-08-11 09:16:00

2022-12-01 09:27:00

無人機(jī)智能

2024-07-30 08:00:00

Kubernetes數(shù)據(jù)庫(kù)
點(diǎn)贊
收藏

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

色偷偷偷综合中文字幕;dd| 亚洲午夜一二三区视频| 国产精品中文字幕久久久| 9999热视频| 欧美日日夜夜| 欧美片在线播放| 中文字幕无码精品亚洲资源网久久| 天堂在线资源8| 美女国产一区二区三区| 欧美激情一区二区三区成人| 久久久久久国产免费a片| 欧州一区二区三区| 疯狂蹂躏欧美一区二区精品| www.午夜色| 每日更新在线观看av| 美女免费视频一区二区| 午夜精品一区二区三区在线| 性生交大片免费全黄| 群体交乱之放荡娇妻一区二区| 欧美喷水一区二区| 国产免费999| 黄色漫画在线免费看| 1000精品久久久久久久久| 秋霞久久久久久一区二区| 超碰人人人人人人| 久国产精品韩国三级视频| 97视频在线观看视频免费视频 | 黄色片网站免费在线观看| 日本vs亚洲vs韩国一区三区二区 | 最新欧美色图| 亚洲亚洲人成综合网络| 黑人巨大国产9丨视频| 成人性生交大片免费看午夜| 99国产精品久久久久久久久久久| 亚洲资源在线看| 国产一区二区小视频| 日精品一区二区三区| 亚洲18私人小影院| 欧美丰满艳妇bbwbbw| 亚洲成av人片一区二区密柚| 一区二区三区 在线观看视| 91精彩刺激对白露脸偷拍| 国产精品白浆| 制服丝袜av成人在线看| 黄色一级免费大片| 校园春色亚洲色图| 日韩欧美成人网| 日韩在线综合网| 爱啪视频在线观看视频免费| 亚洲综合999| 妞干网在线播放| 欧美aaa免费| 亚洲亚洲精品在线观看| 欧美乱大交xxxxx潮喷l头像| 丝袜综合欧美| 亚洲第一成人在线| 国产亚洲欧美在线视频| 欧美大电影免费观看| 色婷婷亚洲精品| 波多结衣在线观看| 亚洲一区av| 日韩欧美一级精品久久| 少妇搡bbbb搡bbb搡打电话| 在线精品自拍| 日韩成人在线电影网| 黄色工厂在线观看| 精品国产一区二区三区久久久蜜臀| 亚洲欧美综合精品久久成人| 欧美高清性xxxx| 国产欧美日韩视频在线| 亚洲欧美精品在线| 国产精品福利导航| 伊人久久大香线蕉综合网蜜芽| 亚洲精品永久免费| 综合 欧美 亚洲日本| 欧美~级网站不卡| 久久99久国产精品黄毛片入口| 久视频在线观看| 亚洲综合激情| 国产免费久久av| www.97av.com| 久久久久亚洲蜜桃| www.午夜色| 欧美sm一区| 欧美日本一道本| 亚洲精品激情视频| 欧美色图一区| 欧美激情一级欧美精品| 波多野结衣视频在线看| 国产成人免费看一级大黄| 日韩系列在线| 尤物yw午夜国产精品视频| 久久av红桃一区二区禁漫| 好吊日精品视频| 国产www精品| 国产三级在线观看视频| 91免费小视频| 欧美 亚洲 视频| 二吊插入一穴一区二区| 日韩一区二区三区三四区视频在线观看| 91传媒理伦片在线观看| 日本道不卡免费一区| 隔壁老王国产在线精品| 一区二区日韩视频| 99热在这里有精品免费| 中文字幕欧美日韩一区二区| 日本乱码一区二区三区不卡| 欧美乱熟臀69xxxxxx| 日本黄色网址大全| 欧美精品激情| 国产日韩精品一区二区| 日韩偷拍自拍| 亚洲一二三四在线观看| 国产一二三区av| 日韩理论电影中文字幕| 九九综合九九综合| 91女人18毛片水多国产| 久久久青草青青国产亚洲免观| 成人国产在线看| 欧美成人家庭影院| 国产午夜精品免费一区二区三区| 久久精品无码人妻| 国产成人一区在线| 中文字幕剧情在线观看一区| 91在线亚洲| 亚洲理论在线a中文字幕| 久久免费视频99| 国产在线视视频有精品| 亚洲精品成人三区| 欧美xxxxxx| 亚洲美女精品成人在线视频| 国产香蕉在线视频| 成人性生交大片免费看中文网站| 五月天色婷婷综合| 国产精品无码人妻一区二区在线 | 国产精品一香蕉国产线看观看| 天天干视频在线观看| 亚洲激情校园春色| 97超碰人人看| 91精品精品| 成人网在线免费观看| a√在线中文网新版址在线| 欧美日韩在线视频一区| a级一a一级在线观看| 激情综合在线| 精品国产91亚洲一区二区三区www| 欧美性受ⅹ╳╳╳黑人a性爽| 在线不卡a资源高清| 中文乱码字幕高清一区二区| 久久精品国产99| 2025韩国大尺度电影| 中文字幕综合| 精品中文字幕在线观看| 亚洲国产999| 蜜臀av午夜精品| 国产欧美日韩卡一| 99sesese| 欧美三级不卡| 国产视色精品亚洲一区二区| 深夜福利视频一区二区| 亚洲精品一区二区久| 久久亚洲精品石原莉奈 | www.在线成人| 免费欧美一级视频| av一区二区高清| 国产一区二区在线播放| 黄色片免费在线观看| 欧美一级高清大全免费观看| 欧美一级高潮片| 91丝袜美腿高跟国产极品老师 | 国产一级一级国产| 欧美国产丝袜视频| 亚洲欧美日韩网站| 99国产精品私拍| 日韩成人av网站| 日韩在线你懂得| 久久久久国产精品免费| 男女污污视频在线观看| 欧美手机在线视频| 亚洲一区二区三区蜜桃| 视频一区二区不卡| 青春草在线视频免费观看| 日韩在线观看中文字幕| 69国产精品成人在线播放| 国产在线观看免费网站| 欧美精品123区| 久久精品人妻一区二区三区| 久久久精品免费免费| 欧美性受xxxx黒人xyx性爽| 亚洲神马久久| 婷婷四房综合激情五月| 2021年精品国产福利在线| 日韩av电影手机在线| 国产激情在线| 日韩av一卡二卡| 97在线播放免费观看| 亚洲成av人片一区二区梦乃| 国产三级在线观看完整版| 国产精品69毛片高清亚洲| 日本久久久精品视频| 99久久久国产精品美女| 国产一区二区三区高清| 日本久久久久| 2024亚洲男人天堂| 韩国中文字幕在线| 亚洲性夜色噜噜噜7777| 亚洲h视频在线观看| 欧洲精品一区二区| 国产精品不卡av| 中文字幕日本不卡| 亚洲成人av免费在线观看| 韩国欧美国产一区| 成人黄色片视频| 国产精品av久久久久久麻豆网| 国产偷久久久精品专区| 久久福利在线| 欧美精品生活片| 福利在线播放| 日韩精品视频在线观看网址| 国产suv精品一区二区69| 欧洲一区在线观看| aaaaaa毛片| 大桥未久av一区二区三区| 国产三级国产精品国产国在线观看| 久久精品夜夜夜夜久久| 亚洲av成人精品一区二区三区| 国产揄拍国内精品对白| 男女视频在线看| 久久久精品日韩| 九色在线视频观看| 亚洲电影在线| 精品一二三四五区| 欧美特黄一区| www.99riav| 欧美激情 亚洲a∨综合| 一本—道久久a久久精品蜜桃| 久久美女精品| 一区不卡字幕| 国产精品久久久久久影院8一贰佰| 视频一区国产精品| 日韩大片在线播放| 污视频在线免费观看一区二区三区| 亚洲va久久| 欧美一区二视频在线免费观看| 青青草久久爱| 欧美日韩精品免费观看视一区二区| 日韩大胆成人| 久久精品99久久| 中文字幕伦av一区二区邻居| 久久久久久久久四区三区| 神马午夜久久| 日本一区美女| 欧美中文一区二区| 一区二区三区|亚洲午夜| 久久国产中文字幕| 中文字幕第一页亚洲| 中文字幕日韩一区二区不卡| 奇米777四色影视在线看| 狠狠久久婷婷| 国产特级黄色大片| 日韩精品一二三四| 久久久久xxxx| 粉嫩av一区二区三区在线播放 | 黄色一级大片在线观看| 日韩精品视频网| 嫩草视频免费在线观看| 国产激情一区二区三区桃花岛亚洲| 97中文字幕在线观看| 91免费视频网| 99热在线观看精品| 亚洲自拍偷拍网站| 黄色av网站免费观看| 欧美久久一二区| 欧美 日韩 国产 精品| 亚洲欧美综合精品久久成人| 秋霞a级毛片在线看| 色综合久久88色综合天天看泰| 黄频免费在线观看| 国产精品久久久久秋霞鲁丝| 国产精品中文| 美女一区视频| 外国成人免费视频| 日本一道本久久| 毛片av一区二区| 亚洲精品第二页| 国产精品人人做人人爽人人添| 99精品久久久久| 色先锋aa成人| 国产高清在线观看视频| 亚洲区中文字幕| 日本资源在线| 国产精品视频男人的天堂| 影音先锋欧美激情| 亚洲精品在线免费| 在线亚洲自拍| 中文字幕无码不卡免费视频| 国产一区二区免费看| 国产精品无码专区| 日韩美女视频一区| 四虎精品永久在线| 日韩一区二区中文字幕| 国模吧精品人体gogo| 欧美成人小视频| 自拍偷自拍亚洲精品被多人伦好爽| 91精品国产99久久久久久红楼 | 蓝色福利精品导航| 亚洲一级av无码毛片精品| 中文字幕亚洲不卡| 无码人妻久久一区二区三区| 日韩欧美精品在线视频| 国产一区二区影视| 韩国一区二区电影| 国产精一区二区| 翔田千里亚洲一二三区| 在线成人亚洲| 欧美人与性动交α欧美精品| 国产免费观看久久| 欧美国产成人精品一区二区三区| 日韩一区二区不卡| 日本精品在线| 国产精品久久91| 日韩精品福利一区二区三区| 日本xxxxx18| 久久国产麻豆精品| 日本激情小视频| 岛国av在线不卡| 亚洲乱码在线观看| 欧美成人网在线| 白嫩亚洲一区二区三区| 相泽南亚洲一区二区在线播放| 噜噜噜躁狠狠躁狠狠精品视频| 91精品人妻一区二区三区蜜桃2| 忘忧草精品久久久久久久高清| 欧美r级在线观看| 日产精品久久久久久久性色| 欧美精品精品精品精品免费| 亚洲国产视频二区| 五月天在线免费视频| 久久精品国内一区二区三区| 国产黄色大片免费看| 色噜噜狠狠成人网p站| 三级在线播放| 26uuu另类亚洲欧美日本老年| 久久97精品| 国产av麻豆mag剧集| 成人免费高清在线观看| 精品无码av在线| 午夜精品影院在线观看| 欧美综合视频在线| 久久久免费在线观看| 亚洲国产高清在线观看| 中文字幕精品在线播放| 国产高清成人在线| 精品无码m3u8在线观看| 精品国产一区二区三区忘忧草| 中文字幕资源网在线观看| 97在线资源站| 亚洲黄色视屏| 在线视频 日韩| 欧美视频国产精品| 国产精品一区二区三区四区色| 日韩av日韩在线观看| 成人区精品一区二区婷婷| 极品粉嫩美女露脸啪啪| 尤物视频一区二区| 熟妇高潮一区二区三区| 欧美亚洲一区在线| 精品av一区二区| 国产aⅴ爽av久久久久| 亚洲精品国产一区二区精华液| 亚洲精品国产精品国| 97欧美精品一区二区三区| 亚洲素人在线| 福利片一区二区三区| 亚洲黄色尤物视频| 台湾av在线二三区观看| 国产精品久久视频| 亚洲色图欧美| 欧美深性狂猛ⅹxxx深喉| 欧美在线影院一区二区| www久久日com| 玖玖玖精品中文字幕| 久久爱另类一区二区小说| 免费无码毛片一区二区app| 日韩精品中文字幕在线播放| 福利一区二区三区视频在线观看| 97碰在线视频| 久久丝袜美腿综合| 国产又黄又粗又长| 97国产在线视频| 日韩国产欧美一区二区| xxxxwww一片| 91久久精品一区二区三| av网站在线免费看推荐| 久久久久欧美| 国产在线一区观看| 成人毛片一区二区三区| 欧美激情aaaa| 国产精品女同一区二区| 精品国产乱码久久久久久免费|