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

庖丁解 InnoDB 之 Redolog

開(kāi)發(fā) 開(kāi)發(fā)工具
為了取得更好的讀寫(xiě)性能,InnoDB會(huì)將數(shù)據(jù)緩存在內(nèi)存中(InnoDB Buffer Pool),對(duì)磁盤(pán)數(shù)據(jù)的修改也會(huì)落后于內(nèi)存,這時(shí)如果進(jìn)程或機(jī)器崩潰,會(huì)導(dǎo)致內(nèi)存數(shù)據(jù)丟失,為了保證數(shù)據(jù)庫(kù)本身的一致性和持久性,InnoDB維護(hù)了REDO LOG。

[[433471]]

數(shù)據(jù)庫(kù)故障恢復(fù)機(jī)制的前世今生一文中提到,今生磁盤(pán)數(shù)據(jù)庫(kù)為了在保證數(shù)據(jù)庫(kù)的原子性(A, Atomic) 和持久性(D, Durability)的同時(shí),還能以靈活的刷盤(pán)策略來(lái)充分利用磁盤(pán)順序?qū)懙男阅埽瑫?huì)記錄REDO和UNDO日志,即ARIES方法。本文將重點(diǎn)介紹REDO LOG的作用,記錄的內(nèi)容,組織結(jié)構(gòu),寫(xiě)入方式等內(nèi)容,希望讀者能夠更全面準(zhǔn)確的理解REDO LOG在InnoDB中的位置。本文基于MySQL 8.0代碼。

一、為什么需要記錄REDO

為了取得更好的讀寫(xiě)性能,InnoDB會(huì)將數(shù)據(jù)緩存在內(nèi)存中(InnoDB Buffer Pool),對(duì)磁盤(pán)數(shù)據(jù)的修改也會(huì)落后于內(nèi)存,這時(shí)如果進(jìn)程或機(jī)器崩潰,會(huì)導(dǎo)致內(nèi)存數(shù)據(jù)丟失,為了保證數(shù)據(jù)庫(kù)本身的一致性和持久性,InnoDB維護(hù)了REDO LOG。修改Page之前需要先將修改的內(nèi)容記錄到REDO中,并保證REDO LOG早于對(duì)應(yīng)的Page落盤(pán),也就是常說(shuō)的WAL,Write Ahead Log。當(dāng)故障發(fā)生導(dǎo)致內(nèi)存數(shù)據(jù)丟失后,InnoDB會(huì)在重啟時(shí),通過(guò)重放REDO,將Page恢復(fù)到崩潰前的狀態(tài)。

二、需要什么樣的REDO

那么我們需要什么樣的REDO呢?首先,REDO的維護(hù)增加了一份寫(xiě)盤(pán)數(shù)據(jù),同時(shí)為了保證數(shù)據(jù)正確,事務(wù)只有在他的REDO全部落盤(pán)才能返回用戶(hù)成功,REDO的寫(xiě)盤(pán)時(shí)間會(huì)直接影響系統(tǒng)吞吐,顯而易見(jiàn),REDO的數(shù)據(jù)量要盡量少。其次,系統(tǒng)崩潰總是發(fā)生在始料未及的時(shí)候,當(dāng)重啟重放REDO時(shí),系統(tǒng)并不知道哪些REDO對(duì)應(yīng)的Page已經(jīng)落盤(pán),因此REDO的重放必須可重入,即REDO操作要保證冪等。最后,為了便于通過(guò)并發(fā)重放的方式加快重啟恢復(fù)速度,REDO應(yīng)該是基于Page的,即一個(gè)REDO只涉及一個(gè)Page的修改。

熟悉的讀者會(huì)發(fā)現(xiàn),數(shù)據(jù)量小是Logical Logging的優(yōu)點(diǎn),而冪等以及基于Page正是Physical Logging的優(yōu)點(diǎn),因此InnoDB采取了一種稱(chēng)為Physiological Logging的方式,來(lái)兼得二者的優(yōu)勢(shì)。所謂Physiological Logging,就是以Page為單位,但在Page內(nèi)以邏輯的方式記錄。舉個(gè)例子,MLOG_REC_UPDATE_IN_PLACE類(lèi)型的REDO中記錄了對(duì)Page中一個(gè)Record的修改,方法如下:

  1. (Page ID,Record Offset,(Filed 1, Value 1) ... (Filed i, Value i) ... ) 

其中,PageID指定要操作的Page頁(yè),Record Offset記錄了Record在Page內(nèi)的偏移位置,后面的Field數(shù)組,記錄了需要修改的Field以及修改后的Value。

由于Physiological Logging的方式采用了物理Page中的邏輯記法,導(dǎo)致兩個(gè)問(wèn)題:

1、需要基于正確的Page狀態(tài)上重放REDO

由于在一個(gè)Page內(nèi),REDO是以邏輯的方式記錄了前后兩次的修改,因此重放REDO必須基于正確的Page狀態(tài)。然而InnoDB默認(rèn)的Page大小是16KB,是大于文件系統(tǒng)能保證原子的4KB大小的,因此可能出現(xiàn)Page內(nèi)容成功一半的情況。InnoDB中采用了Double Write Buffer的方式來(lái)通過(guò)寫(xiě)兩次的方式保證恢復(fù)的時(shí)候找到一個(gè)正確的Page狀態(tài)。這部分會(huì)在之后介紹Buffer Pool的時(shí)候詳細(xì)介紹。

2、需要保證REDO重放的冪等

Double Write Buffer能夠保證找到一個(gè)正確的Page狀態(tài),我們還需要知道這個(gè)狀態(tài)對(duì)應(yīng)REDO上的哪個(gè)記錄,來(lái)避免對(duì)Page的重復(fù)修改。為此,InnoDB給每個(gè)REDO記錄一個(gè)全局唯一遞增的標(biāo)號(hào)LSN(Log Sequence Number)。Page在修改時(shí),會(huì)將對(duì)應(yīng)的REDO記錄的LSN記錄在Page上(FIL_PAGE_LSN字段),這樣恢復(fù)重放REDO時(shí),就可以來(lái)判斷跳過(guò)已經(jīng)應(yīng)用的REDO,從而實(shí)現(xiàn)重放的冪等。

三、REDO中記錄了什么內(nèi)容

知道了InnoDB中記錄REDO的方式,那么REDO里具體會(huì)記錄哪些內(nèi)容呢?為了應(yīng)對(duì)InnoDB各種各樣不同的需求,到MySQL 8.0為止,已經(jīng)有多達(dá)65種的REDO記錄。用來(lái)記錄這不同的信息,恢復(fù)時(shí)需要判斷不同的REDO類(lèi)型,來(lái)做對(duì)應(yīng)的解析。根據(jù)REDO記錄不同的作用對(duì)象,可以將這65中REDO劃分為三個(gè)大類(lèi):作用于Page,作用于Space以及提供額外信息的Logic類(lèi)型。

1、作用于Page的REDO

這類(lèi)REDO占所有REDO類(lèi)型的絕大多數(shù),根據(jù)作用的Page的不同類(lèi)型又可以細(xì)分為,Index Page REDO,Undo Page REDO,Rtree PageREDO等。比如MLOG_REC_INSERT,MLOG_REC_UPDATE_IN_PLACE,MLOG_REC_DELETE三種類(lèi)型分別對(duì)應(yīng)于Page中記錄的插入,修改以及刪除。這里還是以MLOG_REC_UPDATE_IN_PLACE為例來(lái)看看其中具體的內(nèi)容:

其中,Type就是MLOG_REC_UPDATE_IN_PLACE類(lèi)型,Space ID和Page Number唯一標(biāo)識(shí)一個(gè)Page頁(yè),這三項(xiàng)是所有REDO記錄都需要有的頭信息,后面的是MLOG_REC_UPDATE_IN_PLACE類(lèi)型獨(dú)有的,其中Record Offset用給出要修改的記錄在Page中的位置偏移,Update Field Count說(shuō)明記錄里有幾個(gè)Field要修改,緊接著對(duì)每個(gè)Field給出了Field編號(hào)(Field Number),數(shù)據(jù)長(zhǎng)度(Field Data Length)以及數(shù)據(jù)(Filed Data)。

2、作用于Space的REDO

這類(lèi)REDO針對(duì)一個(gè)Space文件的修改,如MLOG_FILE_CREATE,MLOG_FILE_DELETE,MLOG_FILE_RENAME分別對(duì)應(yīng)對(duì)一個(gè)Space的創(chuàng)建,刪除以及重命名。由于文件操作的REDO是在文件操作結(jié)束后才記錄的,因此在恢復(fù)的過(guò)程中看到這類(lèi)日志時(shí),說(shuō)明文件操作已經(jīng)成功,因此在恢復(fù)過(guò)程中大多只是做對(duì)文件狀態(tài)的檢查,以MLOG_FILE_CREATE來(lái)看看其中記錄的內(nèi)容:

同樣的前三個(gè)字段還是Type,Space ID和Page Number,由于是針對(duì)Page的操作,這里的Page Number永遠(yuǎn)是0。在此之后記錄了創(chuàng)建的文件flag以及文件名,用作重啟恢復(fù)時(shí)的檢查。

3、提供額外信息的Logic REDO

除了上述類(lèi)型外,還有少數(shù)的幾個(gè)REDO類(lèi)型不涉及具體的數(shù)據(jù)修改,只是為了記錄一些需要的信息,比如最常見(jiàn)的MLOG_MULTI_REC_END就是為了標(biāo)識(shí)一個(gè)REDO組,也就是一個(gè)完整的原子操作的結(jié)束。

4、REDO是如何組織的

所謂REDO的組織方式,就是如何把需要的REDO內(nèi)容記錄到磁盤(pán)文件中,以方便高效的REDO寫(xiě)入,讀取,恢復(fù)以及清理。我們這里把REDO從上到下分為三層:邏輯REDO層、物理REDO層和文件層。

1) 邏輯REDO層

這一層是真正的REDO內(nèi)容,REDO由多個(gè)不同Type的多個(gè)REDO記錄收尾相連組成,有全局唯一的遞增的偏移sn,InnoDB會(huì)在全局log_sys中維護(hù)當(dāng)前sn的最大值,并在每次寫(xiě)入數(shù)據(jù)時(shí)將sn增加REDO內(nèi)容長(zhǎng)度。如下圖所示:

2 )物理REDO層

磁盤(pán)是塊設(shè)備,InnoDB中也用Block的概念來(lái)讀寫(xiě)數(shù)據(jù),一個(gè)Block的長(zhǎng)度OS_FILE_LOG_BLOCK_SIZE等于磁盤(pán)扇區(qū)的大小512B,每次IO讀寫(xiě)的最小單位都是一個(gè)Block。除了REDO數(shù)據(jù)以外,Block中還需要一些額外的信息,下圖所示一個(gè)Log Block的的組成,包括12字節(jié)的Block Header:前4字節(jié)中Flush Flag占用最高位bit,標(biāo)識(shí)一次IO的第一個(gè)Block,剩下的31個(gè)個(gè)bit是Block編號(hào);之后是2字節(jié)的數(shù)據(jù)長(zhǎng)度,取值在[12,508];緊接著2字節(jié)的First Record Offset用來(lái)指向Block中第一個(gè)REDO組的開(kāi)始,這個(gè)值的存在使得我們對(duì)任何一個(gè)Block都可以找到一個(gè)合法的的REDO開(kāi)始位置;最后的4字節(jié)Checkpoint Number記錄寫(xiě)B(tài)lock時(shí)的next_checkpoint_number,用來(lái)發(fā)現(xiàn)文件的循環(huán)使用,這個(gè)會(huì)在文件層詳細(xì)講解。Block末尾是4字節(jié)的Block Tailer,記錄當(dāng)前Block的Checksum,通過(guò)這個(gè)值,讀取Log時(shí)可以明確Block數(shù)據(jù)有沒(méi)有被完整寫(xiě)完。

Block中剩余的中間498個(gè)字節(jié)就是REDO真正內(nèi)容的存放位置,也就是我們上面說(shuō)的邏輯REDO。我們現(xiàn)在將邏輯REDO放到物理REDO空間中,由于Block內(nèi)的空間固定,而REDO長(zhǎng)度不定,因此可能一個(gè)Block中有多個(gè)REDO,也可能一個(gè)REDO被拆分到多個(gè)Block中,如下圖所示,棕色和紅色分別代表Block Header和Tailer,中間的REDO記錄由于前一個(gè)Block剩余空間不足,而被拆分在連續(xù)的兩個(gè)Block中。

由于增加了Block Header和Tailer的字節(jié)開(kāi)銷(xiāo),在物理REDO空間中用LSN來(lái)標(biāo)識(shí)偏移,可以看出LSN和SN之間有簡(jiǎn)單的換算關(guān)系:

  1. constexpr inline lsn_t log_translate_sn_to_lsn(lsn_t sn) { 
  2.   return (sn / LOG_BLOCK_DATA_SIZE * OS_FILE_LOG_BLOCK_SIZE + 
  3.           sn % LOG_BLOCK_DATA_SIZE + LOG_BLOCK_HDR_SIZE); 

SN加上之前所有的Block的Header以及Tailer的長(zhǎng)度就可以換算到對(duì)應(yīng)的LSN,反之亦然。

3) 文件層

最終REDO會(huì)被寫(xiě)入到REDO日志文件中,以ib_logfile0、ib_logfile1...命名,為了避免創(chuàng)建文件及初始化空間帶來(lái)的開(kāi)銷(xiāo),InooDB的REDO文件會(huì)循環(huán)使用,通過(guò)參數(shù)innodb_log_files_in_group可以指定REDO文件的個(gè)數(shù)。多個(gè)文件收尾相連順序?qū)懭隦EDO內(nèi)容。每個(gè)文件以Block為單位劃分,每個(gè)文件的開(kāi)頭固定預(yù)留4個(gè)Block來(lái)記錄一些額外的信息,其中第一個(gè)Block稱(chēng)為Header Block,之后的3個(gè)Block在0號(hào)文件上用來(lái)存儲(chǔ)Checkpoint信息,而在其他文件上留空:

其中第一個(gè)Header Block的數(shù)據(jù)區(qū)域記錄了一些文件信息,如下圖所示,4字節(jié)的Formate字段記錄Log的版本,不同版本的LOG,會(huì)有REDO類(lèi)型的增減,這個(gè)信息是8.0開(kāi)始才加入的;8字節(jié)的Start LSN標(biāo)識(shí)當(dāng)前文件開(kāi)始LSN,通過(guò)這個(gè)信息可以將文件的offset與對(duì)應(yīng)的lsn對(duì)應(yīng)起來(lái);最后是最長(zhǎng)32位的Creator信息,正常情況下會(huì)記錄MySQL的版本。

現(xiàn)在我們將REDO放到文件空間中,如下圖所示,邏輯REDO是真正需要的數(shù)據(jù),用sn索引,邏輯REDO按固定大小的Block組織,并添加Block的頭尾信息形成物理REDO,以lsn索引,這些Block又會(huì)放到循環(huán)使用的文件空間中的某一位置,文件中用offset索引:

雖然通過(guò)LSN可以唯一標(biāo)識(shí)一個(gè)REDO位置,但最終對(duì)REDO的讀寫(xiě)還需要轉(zhuǎn)換到對(duì)文件的讀寫(xiě)IO,這個(gè)時(shí)候就需要表示文件空間的offset,他們之間的換算方式如下:

  1. const auto real_offset = 
  2.       log.current_file_real_offset + (lsn - log.current_file_lsn); 

切換文件時(shí)會(huì)在內(nèi)存中更新當(dāng)前文件開(kāi)頭的文件offset,current_file_real_offset,以及對(duì)應(yīng)的LSN,current_file_lsn,通過(guò)這兩個(gè)值可以方便地用上面的方式將LSN轉(zhuǎn)化為文件offset。注意這里的offset是相當(dāng)于整個(gè)REDO文件空間而言的,由于InnoDB中讀寫(xiě)文件的space層實(shí)現(xiàn)支持多個(gè)文件,因此,可以將首位相連的多個(gè)REDO文件看成一個(gè)大文件,那么這里的offset就是這個(gè)大文件中的偏移。

五、如何高效地寫(xiě)REDO

作為維護(hù)數(shù)據(jù)庫(kù)正確性的重要信息,REDO日志必須在事務(wù)提交前保證落盤(pán),否則一旦斷電將會(huì)有數(shù)據(jù)丟失的可能,因此從REDO生成到最終落盤(pán)的完整過(guò)程成為數(shù)據(jù)庫(kù)寫(xiě)入的關(guān)鍵路徑,其效率也直接決定了數(shù)據(jù)庫(kù)的寫(xiě)入性能。這個(gè)過(guò)程包括REDO內(nèi)容的產(chǎn)生,REDO寫(xiě)入InnoDB Log Buffer,從InnoDB Log Buffer寫(xiě)入操作系統(tǒng)Page Cache,以及REDO刷盤(pán),之后還需要喚醒等待的用戶(hù)線(xiàn)程完成Commit。下面就通過(guò)這幾個(gè)階段來(lái)看看InnoDB如何在高并發(fā)的情況下還能高效地完成寫(xiě)REDO。

1、REDO產(chǎn)生

我們知道事務(wù)在寫(xiě)入數(shù)據(jù)的時(shí)候會(huì)產(chǎn)生REDO,一次原子的操作可能會(huì)包含多條REDO記錄,這些REDO可能是訪(fǎng)問(wèn)同一Page的不同位置,也可能是訪(fǎng)問(wèn)不同的Page(如Btree節(jié)點(diǎn)分裂)。InnoDB有一套完整的機(jī)制來(lái)保證涉及一次原子操作的多條REDO記錄原子,即恢復(fù)的時(shí)候要么全部重放,要不全部不重放,這部分將在之后介紹恢復(fù)邏輯的時(shí)候詳細(xì)介紹,本文只涉及其中最基本的要求,就是這些REDO必須連續(xù)。InnoDB中通過(guò)min-transaction實(shí)現(xiàn),簡(jiǎn)稱(chēng)mtr,需要原子操作時(shí),調(diào)用mtr_start生成一個(gè)mtr,mtr中會(huì)維護(hù)一個(gè)動(dòng)態(tài)增長(zhǎng)的m_log,這是一個(gè)動(dòng)態(tài)分配的內(nèi)存空間,將這個(gè)原子操作需要寫(xiě)的所有REDO先寫(xiě)到這個(gè)m_log中,當(dāng)原子操作結(jié)束后,調(diào)用mtr_commit將m_log中的數(shù)據(jù)拷貝到InnoDB的Log Buffer。

2、寫(xiě)入InnoDB Log Buffer

高并發(fā)的環(huán)境中,會(huì)同時(shí)有非常多的min-transaction(mtr)需要拷貝數(shù)據(jù)到Log Buffer,如果通過(guò)鎖互斥,那么毫無(wú)疑問(wèn)這里將成為明顯的性能瓶頸。為此,從MySQL 8.0開(kāi)始,設(shè)計(jì)了一套無(wú)鎖的寫(xiě)log機(jī)制,其核心思路是允許不同的mtr,同時(shí)并發(fā)地寫(xiě)Log Buffer的不同位置。不同的mtr會(huì)首先調(diào)用log_buffer_reserve函數(shù),這個(gè)函數(shù)里會(huì)用自己的REDO長(zhǎng)度,原子地對(duì)全局偏移log.sn做fetch_add,得到自己在Log Buffer中獨(dú)享的空間。之后不同mtr并行的將自己的m_log中的數(shù)據(jù)拷貝到各自獨(dú)享的空間內(nèi)。

  1. /* Reserve space in sequence of data bytes: */ 
  2. const sn_t start_sn = log.sn.fetch_add(len); 

3、寫(xiě)入Page Cache

寫(xiě)入到Log Buffer中的REDO數(shù)據(jù)需要進(jìn)一步寫(xiě)入操作系統(tǒng)的Page Cache,InnoDB中有單獨(dú)的log_writer來(lái)做這件事情。這里有個(gè)問(wèn)題,由于Log Buffer中的數(shù)據(jù)是不同mtr并發(fā)寫(xiě)入的,這個(gè)過(guò)程中Log Buffer中是有空洞的,因此log_writer需要感知當(dāng)前Log Buffer中連續(xù)日志的末尾,將連續(xù)日志通過(guò)pwrite系統(tǒng)調(diào)用寫(xiě)入操作系統(tǒng)Page Cache。整個(gè)過(guò)程中應(yīng)盡可能不影響后續(xù)mtr進(jìn)行數(shù)據(jù)拷貝,InnoDB在這里引入一個(gè)叫做link_buf的數(shù)據(jù)結(jié)構(gòu),如下圖所示:

link_buf是一個(gè)循環(huán)使用的數(shù)組,對(duì)每個(gè)lsn取模可以得到其在link_buf上的一個(gè)槽位,在這個(gè)槽位中記錄REDO長(zhǎng)度。另外一個(gè)線(xiàn)程從開(kāi)始遍歷這個(gè)link_buf,通過(guò)槽位中的長(zhǎng)度可以找到這條REDO的結(jié)尾位置,一直遍歷到下一位置為0的位置,可以認(rèn)為之后的REDO有空洞,而之前已經(jīng)連續(xù),這個(gè)位置叫做link_buf的tail。下面看看log_writer和眾多mtr是如何利用這個(gè)link_buf數(shù)據(jù)結(jié)構(gòu)的。這里的這個(gè)link_buf為log.recent_written,如下圖所示:

圖中上半部分是REDO日志示意圖,write_lsn是當(dāng)前l(fā)og_writer已經(jīng)寫(xiě)入到Page Cache中日志末尾,current_lsn是當(dāng)前已經(jīng)分配給mtr的的最大lsn位置,而buf_ready_for_write_lsn是當(dāng)前l(fā)og_writer找到的Log Buffer中已經(jīng)連續(xù)的日志結(jié)尾,從write_lsn到buf_ready_for_write_lsn是下一次log_writer可以連續(xù)調(diào)用pwrite寫(xiě)入Page Cache的范圍,而從buf_ready_for_write_lsn到current_lsn是當(dāng)前mtr正在并發(fā)寫(xiě)Log Buffer的范圍。下面的連續(xù)方格便是log.recent_written的數(shù)據(jù)結(jié)構(gòu),可以看出由于中間的兩個(gè)全零的空洞導(dǎo)致buf_ready_for_write_lsn無(wú)法繼續(xù)推進(jìn),接下來(lái),假如reserve到中間第一個(gè)空洞的mtr也完成了寫(xiě)Log Buffer,并更新了log.recent_written*,如下圖:

這時(shí),log_writer從當(dāng)前的buf_ready_for_write_lsn向后遍歷log.recent_written,發(fā)現(xiàn)這段已經(jīng)連續(xù):

因此提升當(dāng)前的buf_ready_for_write_lsn,并將log.recent_written的tail位置向前滑動(dòng),之后的位置清零,供之后循環(huán)復(fù)用:

緊接log_writer將連續(xù)的內(nèi)容刷盤(pán)并提升write_lsn。

4、刷盤(pán)

log_writer提升write_lsn之后會(huì)通知log_flusher線(xiàn)程,log_flusher線(xiàn)程會(huì)調(diào)用fsync將REDO刷盤(pán),至此完成了REDO完整的寫(xiě)入過(guò)程。

5、喚醒用戶(hù)線(xiàn)程

為了保證數(shù)據(jù)正確,只有REDO寫(xiě)完后事務(wù)才可以commit,因此在REDO寫(xiě)入的過(guò)程中,大量的用戶(hù)線(xiàn)程會(huì)block等待,直到自己的最后一條日志結(jié)束寫(xiě)入。默認(rèn)情況下innodb_flush_log_at_trx_commit = 1,需要等REDO完成刷盤(pán),這也是最安全的方式。當(dāng)然,也可以通過(guò)設(shè)置innodb_flush_log_at_trx_commit = 2,這樣,只要REDO寫(xiě)入Page Cache就認(rèn)為完成了寫(xiě)入,極端情況下,掉電可能導(dǎo)致數(shù)據(jù)丟失。

大量的用戶(hù)線(xiàn)程調(diào)用log_write_up_to等待在自己的lsn位置,為了避免大量無(wú)效的喚醒,InnoDB將阻塞的條件變量拆分為多個(gè),log_write_up_to根據(jù)自己需要等待的lsn所在的block取模對(duì)應(yīng)到不同的條件變量上去。同時(shí),為了避免大量的喚醒工作影響log_writer或log_flusher線(xiàn)程,InnoDB中引入了兩個(gè)專(zhuān)門(mén)負(fù)責(zé)喚醒用戶(hù)的線(xiàn)程:log_wirte_notifier和log_flush_notifier,當(dāng)超過(guò)一個(gè)條件變量需要被喚醒時(shí),log_writer和log_flusher會(huì)通知這兩個(gè)線(xiàn)程完成喚醒工作。下圖是整個(gè)過(guò)程的示意圖:

多個(gè)線(xiàn)程通過(guò)一些內(nèi)部數(shù)據(jù)結(jié)構(gòu)的輔助,完成了高效的從REDO產(chǎn)生,到REDO寫(xiě)盤(pán),再到喚醒用戶(hù)線(xiàn)程的流程,下面是整個(gè)這個(gè)過(guò)程的時(shí)序圖:

六、如何安全地清除REDO

由于REDO文件空間有限,同時(shí)為了盡量減少恢復(fù)時(shí)需要重放的REDO,InnoDB引入log_checkpointer線(xiàn)程周期性的打Checkpoint。重啟恢復(fù)的時(shí)候,只需要從最新的Checkpoint開(kāi)始回放后邊的REDO,因此Checkpoint之前的REDO就可以刪除或被復(fù)用。

我們知道REDO的作用是避免只寫(xiě)了內(nèi)存的數(shù)據(jù)由于故障丟失,那么打Checkpiont的位置就必須保證之前所有REDO所產(chǎn)生的內(nèi)存臟頁(yè)都已經(jīng)刷盤(pán)。最直接的,可以從Buffer Pool中獲得當(dāng)前所有臟頁(yè)對(duì)應(yīng)的最小REDO LSN:lwm_lsn。但光有這個(gè)還不夠,因?yàn)橛幸徊糠謒in-transaction的REDO對(duì)應(yīng)的Page還沒(méi)有來(lái)的及加入到Buffer Pool的臟頁(yè)中去,如果checkpoint打到這些REDO的后邊,一旦這時(shí)發(fā)生故障恢復(fù),這部分?jǐn)?shù)據(jù)將丟失,因此還需要知道當(dāng)前已經(jīng)加入到Buffer Pool的REDO lsn位置:dpa_lsn。取二者的較小值作為最終checkpoint的位置,其核心邏輯如下:

  1. /* LWM lsn for unflushed dirty pages in Buffer Pool */ 
  2. lsn_t lwm_lsn = buf_pool_get_oldest_modification_lwm(); 
  3.  
  4. /* Note lsn up to which all dirty pages have already been added into Buffer Pool */ 
  5. const lsn_t dpa_lsn = log_buffer_dirty_pages_added_up_to_lsn(log); 
  6.  
  7. lsn_t checkpoint_lsn = std::min(lwm_lsn, dpa_lsn); 

MySQL 8.0中為了能夠讓mtr之間更大程度的并發(fā),允許并發(fā)地給Buffer Pool注冊(cè)臟頁(yè)。類(lèi)似與log.recent_written和log_writer,這里引入一個(gè)叫做recent_closed的link_buf來(lái)處理并發(fā)帶來(lái)的空洞,由單獨(dú)的線(xiàn)程log_closer來(lái)提升recent_closed的tail,也就是當(dāng)前連續(xù)加入Buffer Pool臟頁(yè)的最大LSN,這個(gè)值也就是上面提到的dpa_lsn。需要注意的是,由于這種亂序的存在,lwm_lsn的值并不能簡(jiǎn)單的獲取當(dāng)前Buffer Pool中的最老的臟頁(yè)的LSN,保守起見(jiàn),還需要減掉一個(gè)recent_closed的容量大小,也就是最大的亂序范圍,簡(jiǎn)化后的代碼如下:

  1. /* LWM lsn for unflushed dirty pages in Buffer Pool */ 
  2. const lsn_t lsn = buf_pool_get_oldest_modification_approx(); 
  3. const lsn_t lag = log.recent_closed.capacity(); 
  4. lsn_t lwm_lsn = lsn - lag; 
  5.  
  6. /* Note lsn up to which all dirty pages have already been added into Buffer Pool */ 
  7. const lsn_t dpa_lsn = log_buffer_dirty_pages_added_up_to_lsn(log); 
  8.  
  9. lsn_t checkpoint_lsn = std::min(lwm_lsn, dpa_lsn); 

這里有一個(gè)問(wèn)題,由于lwm_lsn已經(jīng)減去了recent_closed的capacity,因此理論上這個(gè)值一定是小于dpa_lsn的。那么再去比較lwm_lsn和dpa_lsn來(lái)獲取Checkpoint位置或許是沒(méi)有意義的。

上面已經(jīng)提到,ib_logfile0文件的前三個(gè)Block有兩個(gè)被預(yù)留作為Checkpoint Block,這兩個(gè)Block會(huì)在打Checkpiont的時(shí)候交替使用,這樣來(lái)避免寫(xiě)Checkpoint過(guò)程中的崩潰導(dǎo)致沒(méi)有可用的Checkpoint。Checkpoint Block中的內(nèi)容如下:

首先8個(gè)字節(jié)的Checkpoint Number,通過(guò)比較這個(gè)值可以判斷哪個(gè)是最新的Checkpiont記錄,之后8字節(jié)的Checkpoint LSN為打Checkpoint的REDO位置,恢復(fù)時(shí)會(huì)從這個(gè)位置開(kāi)始重放后邊的REDO。之后8個(gè)字節(jié)的Checkpoint Offset,將Checkpoint LSN與文件空間的偏移對(duì)應(yīng)起來(lái)。最后8字節(jié)是前面提到的Log Buffer的長(zhǎng)度,這個(gè)值目前在恢復(fù)過(guò)程并沒(méi)有使用。

七、總結(jié)

本文系統(tǒng)的介紹了InnoDB中REDO的作用、特性、組織結(jié)構(gòu)、寫(xiě)入方式已經(jīng)清理時(shí)機(jī),基本覆蓋了REDO的大多數(shù)內(nèi)容。關(guān)于重啟恢復(fù)時(shí)如何使用REDO將數(shù)據(jù)庫(kù)恢復(fù)到正確的狀態(tài),將在之后介紹InnoDB故障恢復(fù)機(jī)制的時(shí)候詳細(xì)介紹。

參考

[1] MySQL 8.0.11Source Code Documentation: Format of redo log

https://dev.mysql.com/doc/dev/mysql-server/8.0.11/PAGE_INNODB_REDO_LOG_FORMAT.html?spm=ata.21736010.0.0.600e6f95JcmTlA

[2] MySQL 8.0: New Lock free, scalable WAL design

https://mysqlserverteam.com/mysql-8-0-new-lock-free-scalable-wal-design/?spm=ata.21736010.0.0.600e6f95JcmTlA

[3] How InnoDB handles REDO logging

https://www.percona.com/blog/2011/02/03/how-innodb-handles-redo-logging/?spm=ata.21736010.0.0.600e6f95JcmTlA

[4] MySQL Source Code

https://github.com/mysql/mysql-server?spm=ata.21736010.0.0.600e6f95JcmTlA

[5] 數(shù)據(jù)庫(kù)故障恢復(fù)機(jī)制的前世今生

http://catkang.github.io/2019/01/16/crash-recovery.html?spm=ata.21736010.0.0.600e6f95JcmTlA

責(zé)任編輯:武曉燕 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2021-11-09 10:34:46

InnoDB數(shù)據(jù)并發(fā)

2017-12-20 08:58:14

LevelDB概覽思路

2021-01-20 14:06:54

華為云

2021-09-10 11:12:50

開(kāi)發(fā)技能代碼

2021-09-14 09:35:34

MySQL查詢(xún)解析優(yōu)化器

2011-12-14 18:28:10

惠普

2025-09-24 08:03:22

2025-10-27 01:33:00

CPU全鏈路命令

2018-03-14 17:28:51

WOT測(cè)試基礎(chǔ)架構(gòu)茹炳晟

2025-05-14 08:10:00

redo logMySQL重做日志

2024-03-08 16:27:22

領(lǐng)域事件DDD項(xiàng)目跨層解耦

2021-06-30 18:16:38

MySQLWal策略

2011-06-17 14:11:47

服務(wù)器交換機(jī)病毒

2019-09-04 08:13:53

MySQLInnodb事務(wù)系統(tǒng)

2009-01-22 12:15:03

NetApp數(shù)據(jù)管理企業(yè)管理

2024-01-03 09:48:15

駕駛技術(shù)架構(gòu)

2025-08-29 07:58:42

2021-12-26 18:24:51

MySQL InnoDB引擎

2009-11-23 09:19:26

LINUX安全GRUB加密

2013-06-03 09:36:04

點(diǎn)贊
收藏

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

久久久久久电影| 一区二区三区在线观看免费| 日韩欧美国产成人| 午夜老司机精品| av网站在线免费看| 亚洲一区二区伦理| 久久视频在线观看免费| 伊人网综合视频| jizz久久久久久| 亚洲综合一区二区精品导航| 日本一区二区三区www| 99产精品成人啪免费网站| 久久动漫亚洲| 欧美激情亚洲国产| 成年人免费视频播放| 另类春色校园亚洲| 欧美日韩精品专区| 精品久久久久久久免费人妻| 日韩特级毛片| 国产精品美女久久久久高潮| 久久精品日韩| 亚洲国产精品欧美久久 | 欧美视频免费看欧美视频| 1区2区3区在线观看| 26uuu亚洲| 成人国产1314www色视频| 在线观看色网站| 久久午夜av| 国内精品久久久久久中文字幕| 亚洲天堂精品一区| 精品国精品国产自在久国产应用 | 国产精品久久久久久久7电影| 麻豆视频在线观看| 中文乱码免费一区二区三区下载| 一区二区欧美久久| 三级网站在线免费观看| 久久99精品国产自在现线| 91精品国产高清一区二区三区蜜臀| 搡女人真爽免费午夜网站| 啊啊啊久久久| 天天影视涩香欲综合网| av一区二区三区免费观看| 91麻豆一二三四在线| 中文字幕一区二区三区不卡在线| 日韩三级电影| 97人人在线| 国产精品视频麻豆| 亚洲a∨一区二区三区| 国模精品一区二区| 国产亲近乱来精品视频| 日本视频一区二区在线观看| 久久手机免费观看| 久久九九国产精品| 品久久久久久久久久96高清| 国产小视频免费在线观看| 国产亚洲一区二区在线观看| 欧美下载看逼逼| 黄色美女网站在线观看| 国产色产综合色产在线视频| 少妇特黄a一区二区三区| 91亚洲欧美| 亚洲欧美在线aaa| 成人国产一区二区三区| 羞羞视频在线免费国产| 亚洲综合激情另类小说区| 久久99久久久久久| 天堂在线中文网官网| 色欧美88888久久久久久影院| 国产v亚洲v天堂无码久久久 | 超碰在线网址| 亚洲一区av在线| 国产免费黄色av| 精品免费av一区二区三区| 欧美日本在线看| 韩国三级在线播放| 欧美大奶一区二区| 国产一区二区日韩| 日本福利片在线观看| 激情综合电影网| 日韩av免费在线播放| 在线播放成人av| 成人自拍视频在线| 欧美日韩在线一区二区三区| 欧美日本高清| 婷婷综合在线观看| 久久久精品麻豆| 2021年精品国产福利在线| 国产视频自拍一区| 青花影视在线观看免费高清| 在线一区欧美| 国产日韩欧美成人| 熟妇人妻系列aⅴ无码专区友真希| 久久久精品免费观看| 福利网在线观看| 一本大道色婷婷在线| 欧美精品高清视频| 男人网站在线观看| 久久国产成人精品| 国产做受高潮69| 国产又大又粗又硬| 97久久精品人人做人人爽50路| 亚洲国产精品久久久久久女王| 在线观看的网站你懂的| 在线观看视频一区| 中文在线观看免费视频| 99久久精品网站| 欧美最猛性xxxxx(亚洲精品)| 国产三级三级在线观看| 久久精品视频免费观看| 国产美女作爱全过程免费视频| 欧美色网在线| 亚洲精品一区二区三区99| 中国美女黄色一级片| 亚洲一区久久| 成人精品一二区| 欧美三级电影一区二区三区| 欧美日韩在线视频首页| 日本黄色三级网站| 欧美电影《睫毛膏》| 日本精品va在线观看| 国产按摩一区二区三区| 中文字幕成人在线观看| 日本免费一级视频| 动漫av一区| 欧美精品在线免费| 一区不卡在线观看| 国产日韩一级二级三级| 哪个网站能看毛片| 菁菁伊人国产精品| 高清一区二区三区日本久| 国产三级按摩推拿按摩| 日韩理论片在线| 一区二区xxx| 欧美精品一二| 国产91亚洲精品| 九色视频在线播放| 日韩欧美极品在线观看| 北岛玲一区二区| 99综合在线| 国产欧美日韩综合一区在线观看| 色呦呦呦在线观看| 日韩欧美国产不卡| 免费中文字幕在线观看| 国产·精品毛片| youjizz.com在线观看| 美女精品久久| 欧美日韩成人在线视频| 高h调教冰块play男男双性文| 一区二区久久久久| 成人做爰www看视频软件| 国产一区二区三区自拍| 国产欧美日韩在线播放| 国产免费拔擦拔擦8x高清在线人| 亚洲国产精品久久久久秋霞蜜臀| 国产精品成人aaaa在线| 26uuu国产在线精品一区二区| 男人操女人逼免费视频| 九一国产精品| 国产精品视频地址| 大片免费在线观看| 欧美成人精品福利| 成年免费在线观看| 国产日韩欧美高清| 五月天激情播播| 亚洲性感美女99在线| 精品久久久久久中文字幕动漫| 在线播放高清视频www| 国产一区二区三区丝袜| 一级黄色免费看| 亚洲一二三区在线观看| 人妻少妇一区二区| 欧美aa在线视频| 成人在线免费观看网址| 好吊妞视频这里有精品| 日本久久精品视频| 日本高清视频在线观看| 精品久久五月天| 一级成人黄色片| 国产精品欧美一区喷水| 波多野结衣电影免费观看| 国产精品夜夜夜| 亚洲 国产 欧美一区| 2021年精品国产福利在线| 日本久久中文字幕| 超碰在线网址| 亚洲人成在线观看网站高清| 国产精品无码专区av免费播放| 亚洲成av人**亚洲成av**| 日本精品在线观看视频| 国产成人av电影在线| 欧美少妇性生活视频| 欧美一区视频| 日韩欧美亚洲区| ccyy激情综合| 国产日韩欧美成人| 人在线成免费视频| 久久精品国产91精品亚洲| 天堂在线免费av| 91精品国产入口在线| 免费的毛片视频| 亚洲一区二区三区免费视频| 性猛交娇小69hd| aaa亚洲精品| 亚洲成人av免费观看| 日韩精品一级二级 | 日本不卡一区二区三区在线观看| 欧美a级大片在线| 国产精品免费观看在线| av免费不卡| 美女啪啪无遮挡免费久久网站| 国产在线一二三| 日韩精品极品视频| 精品国产亚洲一区二区麻豆| 欧美日韩一级视频| 秋霞精品一区二区三区| 亚洲影院免费观看| 成人自拍小视频| 国产精品三级av| 人妻av无码一区二区三区| 高清不卡一区二区在线| 婷婷中文字幕在线观看| 热久久国产精品| 国产黄色特级片| 久久久久91| 国产午夜伦鲁鲁| 亚洲激情成人| 日韩精品在线中文字幕| 一区二区影院| 中文视频一区视频二区视频三区| 成人在线国产| 日本亚洲导航| 国产真实有声精品录音| 欧美成ee人免费视频| 精品精品国产三级a∨在线| 国产欧美 在线欧美| 久久青草伊人| 欧美又大又粗又长| 中文在线资源| 91成人在线观看国产| 麻豆蜜桃在线观看| 97视频com| 日韩脚交footjobhd| 69久久夜色精品国产7777| 国产丝袜在线观看视频| 久久久亚洲影院你懂的| 欧美aaaaaaa| 97国产在线视频| 色综合桃花网| 国产成人在线播放| 电影一区二区| 91久久精品国产91性色| 久久免费福利| 国产一区不卡在线观看| 美女主播精品视频一二三四| 久久99精品久久久久久秒播放器 | 亚洲一卡二卡三卡| 日韩88av| 国产日韩欧美大片| 99国产精品视频免费观看一公开| 1024av视频| 日韩**一区毛片| 中文字幕日韩综合| 国产成人福利片| 国产又黄又粗又猛又爽的视频 | 亚洲国产成人精品久久久国产成人一区| 亚洲av无码一区二区三区性色| 精品国偷自产国产一区| 日韩资源在线| 色婷婷久久一区二区| 最新黄网在线观看| **欧美日韩vr在线| 国产原创一区| 国产福利久久| 精品美女久久久| 日本久久久网站| 日韩精品乱码av一区二区| 91在线第一页| 91毛片在线观看| 天海翼在线视频| 精品美女久久久久久免费| 少妇一级淫片日本| 日韩一区二区三区在线视频| 亚洲av成人无码久久精品老人| 在线观看亚洲区| www555久久| 国产精品视频网站| 国产亚洲精品美女久久| 日韩av在线电影观看| 国内精品美女在线观看| 欧美少妇性生活视频| 懂色av中文一区二区三区| 中文字幕免费高清| 亚洲午夜一区二区三区| 中文字幕在线观看你懂的| 精品国产乱码久久久久久1区2区| 国产在线观看高清视频| 欧美激情视频网| 九七电影院97理论片久久tvb| 精品免费日产一区一区三区免费| 五月开心六月丁香综合色啪| 欧美性大战久久久久xxx | yy111111少妇影院日韩夜片| 欧美综合视频| 成年人视频观看| 国产jizzjizz一区二区| 538精品视频| 欧美日韩在线看| 亚洲精品18p| 久久五月天综合| 成人黄色在线| 欧美一进一出视频| 一区二区三区精品视频在线观看| 亚洲国产综合av| 中文字幕在线播放不卡一区| 波多野结衣家庭主妇| 日韩精品免费观看| 欧美hdxxx| 成人av中文| 欧美激情1区2区| 在线免费观看av网| 中文一区在线播放| 好看的av在线| 亚洲精品成人久久电影| 国产网红女主播精品视频| 91亚洲精品久久久久久久久久久久 | 欧美人与性动xxxx| av资源种子在线观看| 日本在线观看天堂男亚洲| 亚洲精品白浆高清| 2018日日夜夜| 成人久久视频在线观看| 久久久久成人网站| 日韩区在线观看| 影音先锋在线播放| 98国产高清一区| 国产中文一区| 欧美大喷水吹潮合集在线观看| 亚洲国产另类精品专区| 亚洲成a人片在线| 欧美激情按摩在线| xvideos.蜜桃一区二区| 国产免费裸体视频| 成人综合婷婷国产精品久久蜜臀| 久久国产在线观看| 亚洲第一精品夜夜躁人人躁 | 无码一区二区三区视频| 99999精品| 一区二区三区.www| 欧美性受xxxx狂喷水| 97国产精品免费视频| 色综合久久中文| 黄色av免费在线播放| 国产精品污网站| 一级黄色短视频| 欧美俄罗斯乱妇| 欧洲亚洲成人| 欧美午夜性生活| 亚洲同性gay激情无套| 亚洲国产精品二区| 欧美性一区二区三区| 精品视频免费| 亚洲天堂av一区二区| 一区二区三区欧美| 三级视频在线看| 国产精品999999| 亚洲精品午夜av福利久久蜜桃| 在线观看一区二区三区视频| 亚洲成a天堂v人片| 蜜桃视频在线观看视频| 成人免费网站在线| 亚洲无吗在线| 日韩中文字幕有码| 欧美一级黄色录像| 天堂√中文最新版在线| 先锋影音日韩| 成人性生交大合| 国产免费一区二区三区四区五区 | 97成人资源站| 亚洲国产一区自拍| 国产精品麻豆成人av电影艾秋| 成人高清dvd| 久久久午夜电影| 国产成人精品一区二三区四区五区 | 欧美午夜视频在线| 国产精品996| 国产精品欧美综合| 欧美极品少妇xxxxx| 成人影院天天5g天天爽无毒影院| 亚洲欧洲日韩综合| 色8久久精品久久久久久蜜| av文字幕在线观看| 欧美精品一区二区视频| 国产麻豆一精品一av一免费| 黄色片网站在线免费观看| 久久69精品久久久久久国产越南| 亚洲美女久久| 在线精品视频播放| 在线播放欧美女士性生活| 另类专区亚洲| 精品国偷自产一区二区三区|