欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

mysql中 redo日志詳解

 更新時間:2024年06月09日 11:47:19   作者:韓朝洋  
mysql都會把回滾時所需的東西記錄下來,這些為了回滾而記錄的東西稱之為撤銷日志,也就是我們今天要講的undo日志,下面我們就來聊一聊這個undo日志,感興趣的朋友跟隨小編一起看看吧

大家好。上篇文章我們介紹了什么是redo日志以及redo日志的寫入過程。建議沒看過上篇文章的同學(xué)先看一下上一篇文章,今天我們繼續(xù)來說一說redo日志。

一、redo日志文件

1. redo日志刷盤時機

我們知道m(xù)tr運行過程中產(chǎn)生的一組redo日志在mtr結(jié)束時會被復(fù)制到log buffer中,可是這些日志總在內(nèi)存里呆著也不是個辦法,在一些情況下它們會被刷新到磁盤里,比如:

  • log buffer空間不足時:log buffer的大小是有限的(通過系統(tǒng)變量 innodb_log_buffer_size 指定),如果不停的往這個有限大小的log buffer 里塞入日志,很快它就會被填滿。InnoDB認(rèn)為如果當(dāng)前寫入log buffer 的redo日志量已經(jīng)占滿了log buffer總?cè)萘康拇蠹s一半左右,就需要把這些日志刷新到磁盤上。
  • 事務(wù)提交時:我們前邊說過之所以使用redo日志主要是因為它占用的空間少,還是順序?qū)懀谑聞?wù)提交時可以不把修改過的Buffer Pool頁面刷新到磁盤,但是為了保證持久性,必須要把修改這些頁面對應(yīng)的redo日志刷新到磁盤。
  • 后臺線程不停的刷刷刷:后臺有一個線程,大約每秒都會刷新一次log buffer中的redo日志到磁盤。
  • 正常關(guān)閉服務(wù)器時。
  • 做所謂的checkpoint時(一會兒介紹過checkpoint的概念)
  • 其他的一些情況…

2. redo日志文件組

MySQL的數(shù)據(jù)目錄(使用 SHOW VARIABLES LIKE ‘datadir’ 查看)下默認(rèn)有兩個名為ib_logfile0和ib_logfile1的文件, log buffer中的日志默認(rèn)情況下就是刷新到這兩個磁盤文件中。如果我們對默認(rèn)的 redo 日志文件不滿意,可以通過下邊幾個啟動參數(shù)來調(diào)節(jié):

innodb_log_group_home_dir

該參數(shù)指定了redo 日志文件所在的目錄,默認(rèn)值就是當(dāng)前的數(shù)據(jù)目錄。

innodb_log_file_size

該參數(shù)指定了每個redo日志文件的大小。

innodb_log_files_in_group

該參數(shù)指定redo日志文件的個數(shù),默認(rèn)值為2,最大值為100。

從上邊的描述中可以看到,磁盤上的redo日志文件不只一個,而是以一個日志文件組的形式出現(xiàn)的。這些文件以ib_logfile[數(shù)字]的形式進(jìn)行命名。在將redo日志寫入日志文件組時,是 從ib_logfile0開始寫,如果 ib_logfile0 寫滿了,寫入ib_logfile1中,依此類推。如果寫到最后一個文件,那就重新轉(zhuǎn)到ib_logfile0繼續(xù)寫,所以整個過程如下圖所示:

總共的redo 日志文件大小其實就是:innodb_log_file_size × innodb_log_files_in_group。

3. redo日志文件格式

log buffer本質(zhì)上是一片連續(xù)的內(nèi)存空間,被劃分成了若干個512字節(jié)大小的block。將log buffer中的redo日志刷新到磁盤的本質(zhì)就是把block的鏡像寫入日志文件中,所以redo日志文件其實也是由若干個512 字節(jié)大小的block組成。

redo日志文件組中的每個文件大小都一樣,格式也一樣,都是由兩部分組成:

  • 前2048個字節(jié),也就是前4個block是用來存儲一些管理信息的。
  • 從第2048字節(jié)往后是用來存儲log buffer中的block鏡像的。

所以我們前邊所說的循環(huán)使用redo日志文件,其實是從每個日志文件的第2048個字節(jié)開始算,畫個示意圖就是這樣:

下面我們介紹一下每個redo日志文件前2048個字節(jié),也就是前4個特殊block的格式都是干嘛的,廢話少說,直接看圖:

從圖中可以看出來,這4個block分別是:

log file header: 描述該 redo 日志文件的一些整體屬性,看一下它的結(jié)構(gòu):

各個屬性的具體釋義如下:

屬性名長度(字節(jié))描述
LOG_HEADER_FORMAT4redo 日志的版本
LOG_HEADER_PAD14用于字節(jié)填充,沒有什么意義。
LOG_HEADER_START_LSN8標(biāo)記本 redo 日志文件開始的LSN值,也就是文件偏移量為2048字節(jié)初處對應(yīng)的LSN值
LOG_HEADER_CREATOR32一個字符串, 標(biāo)記本redo 日志文件的創(chuàng)建者是誰。正常運行時該值為MySQL 的版本號,比如:“MySQL 5.7.21” ,使用 mysqlbackup 命令創(chuàng)建的 redo 日志文件的該值為 “ibbackup” 和創(chuàng)建時間。
LOG_BLOCK_CHECKSUM4本block的校驗值,所有block都有,我們不關(guān)心

checkpoint1: 記錄關(guān)于 checkpoint 的一些屬性,結(jié)構(gòu)如下所示:

屬性長度(字節(jié))描述
LOG_CHECKPOINT_NO8服務(wù)器做 checkpoint 的編號,每做 一次checkpoint ,該值就加1。
LOG_CHECKPOINT_LSN8服務(wù)器做 checkpoint 結(jié)束時對應(yīng)的 LSN 值,系 統(tǒng)崩潰恢復(fù)時將從該值開始。
LOG_CHECKPOINT_OFFSET8上個屬性中的 LSN 值在 redo 日志文件組中的 偏移量
LOG_CHECKPOINT_LOG_BUF_SIZE8服務(wù)器在做 checkpoint 操作時對應(yīng)的 log buffer 的大小
LOG_BLOCK_CHECKSUM4本block的校驗值,所有block都有,我們不關(guān)心

checkpoint1: 記錄關(guān)于 checkpoint 的一些屬性,結(jié)構(gòu)如下所示:

屬性長度(字節(jié))描述
LOG_CHECKPOINT_NO8服務(wù)器做 checkpoint 的編號,每做 一次checkpoint ,該值就加1。
LOG_CHECKPOINT_LSN8服務(wù)器做 checkpoint 結(jié)束時對應(yīng)的 LSN 值,系 統(tǒng)崩潰恢復(fù)時將從該值開始。
LOG_CHECKPOINT_OFFSET8上個屬性中的 LSN 值在 redo 日志文件組中的 偏移量
LOG_CHECKPOINT_LOG_BUF_SIZE8服務(wù)器在做 checkpoint 操作時對應(yīng)的 log buffer 的大小
LOG_BLOCK_CHECKSUM4本block的校驗值,所有block都有,我們不關(guān)心

第三個block未使用,忽略~

checkpoint2: 結(jié)構(gòu)和checkpoint1一樣。

二、Log Sequeue Number

自mysql開始運行,就會不斷的生成redo日志。所以redo日志的量在不斷的遞增,永遠(yuǎn)不會縮減。Inno為記錄已經(jīng)寫入的redo日志量,設(shè)計了一個稱之為Log Sequeue Number的全局變量–日志序列號,簡稱 lsn 。InnoDB規(guī)定初始的lsn值為8704。

我們知道在向log buffer中寫入redo日志時不是一條一條寫入的,而是以一個mtr生成的一組redo日志為單位進(jìn)行寫入的。而且實際上是把日志內(nèi)容寫在了log block body處。但是在統(tǒng)計lsn的增長量時,是按照實際 寫入的日志量加上占用的log block header和log block trailer來計算的。我們來看一個例子:

系統(tǒng)第一次啟動后初始化log buffer時, buf_free(就是標(biāo)記下一條 redo日志應(yīng)該寫入到log buffer的位置的變量)就會指向第一個block的偏移量為12字節(jié)(log block header 的大?。┑牡胤?,那么 lsn 值也會跟著增加12:

如果某個mtr產(chǎn)生的一組redo日志占用的存儲空間比較小,也就是待插入的block剩余空閑空間能容納這個mtr提交的日志時,lsn增長的量就是該mtr生成的redo日志占用的字節(jié)數(shù),假設(shè)mtr_1產(chǎn)生的redo日志量為200字節(jié),那么lsn就要在8716的基礎(chǔ)上增加200 ,變?yōu)?916 。就像這樣:

如果某個mtr產(chǎn)生的一組redo日志占用的存儲空間比較大,也就是待插入的block剩余空閑空間不足以容納這個mtr提交的日志時,lsn增長的量就是該mtr 生成的redo日志占用的字節(jié)數(shù)加上額外占用的log block header和log block trailer的字節(jié)數(shù),就像這樣:

所以:每一組由mtr生成的redo日志都有一個唯一的LSN值與其對應(yīng),LSN值越小,說明 redo日志產(chǎn)生的越早。

1. flushed_to_disk_lsn

redo日志是首先寫到log buffer中,之后才會被刷新到磁盤上的redo 日志文件。所以InnoDB提出了一個稱之為buf_next_to_write的全局變量,標(biāo)記當(dāng)前l(fā)og buffer中已經(jīng)有哪些日志被刷新到磁盤中了。畫個圖表示就是這樣:

我們前邊說lsn 是表示當(dāng)前系統(tǒng)中寫入的redo日志量,這包括了寫到log buffer而沒有刷新到磁盤的日志, 相應(yīng)的,InnoDB提出了一個表示刷新到磁盤中的redo日志量的全局變量,稱之為flushed_to_disk_lsn。
系統(tǒng)第一次啟動時,該變量的值和初始的lsn值是相同的,都是 8704 。隨著系統(tǒng)的運行,redo日志被不斷寫入log buffer,但是并不會立即刷新到磁盤, lsn的值就和flushed_to_disk_lsn的值拉開了差距。我們演示一下:
系統(tǒng)第一次啟動后,向log buffer中寫入了 mtr_1、mtr_2、mtr_3這三個mtr產(chǎn)生的redo日志,假設(shè)這三個mtr開始和結(jié)束時對應(yīng)的lsn值分別是:

mtr_1:8716 ~ 8916。
mtr_2:8916 ~ 9948。
mtr_3:9948 ~ 10000。

此時的lsn 已經(jīng)增長到了10000,但是由于沒有刷新操作,所以此時flushed_to_disk_lsn的值仍為 8704 ,如圖:

隨后進(jìn)行將log buffer中的block刷新到redo日志文件的操作,假設(shè)將 mtr_1和mtr_2的日志刷新到磁盤,那么flushed_to_disk_lsn就應(yīng)該增長 mtr_1和mtr_2寫入的日志量,所以flushed_to_disk_lsn的值增長到了9948,如圖:

綜上所述,當(dāng)有新的redo 日志寫入到log buffer 時,首先lsn的值會增長,但flushed_to_disk_lsn不變, 隨后隨著不斷有l(wèi)og buffer中的日志被刷新到磁盤上, flushed_to_disk_lsn的值也跟著增長。如果兩者的值相同時,說明log buffer中的所有redo日志都已經(jīng)刷新到磁盤中了。

2. lsn值和redo日志文件偏移量的對應(yīng)關(guān)系

因為lsn的值是代表系統(tǒng)寫入的redo日志量的一個總和,一個mtr中產(chǎn)生多少日志,lsn的值就增加多少,這樣 mtr 產(chǎn)生的日志寫到磁盤中時,很容 易計算某一個lsn值在redo日志文件組中的偏移量,如圖:

初始時的LSN值是8704,對應(yīng)文件偏移量2048,之后每個mtr向磁盤中寫入多少字節(jié)日志,lsn的值就增長多少。

3. lsn值和redo日志文件偏移量的對應(yīng)關(guān)系

我們知道一個mtr代表一次對底層頁面的原子訪問,在訪問過程中可能會產(chǎn)生一組不可分割的redo日志,在mtr結(jié)束時,會把這一組redo日志寫入到log buffer中。除此之外,在mtr結(jié)束時還要把在mtr執(zhí)行過程中可能修改過的頁面加入到Buffer Pool的flush鏈表。我們看一下圖:

當(dāng)?shù)谝淮涡薷哪硞€緩存在Buffer Pool中的頁面時,就會把這個頁面對應(yīng)的控制塊插入到flush鏈表的頭部,之后再修改該頁面時由于它已經(jīng)在flush鏈表中了,就不再次插入了。也就是說flush鏈表中的臟頁是按照頁面的第一次修改時間從大到小進(jìn)行排序的。

在這個過程中會在緩存頁對應(yīng)的控制塊中記錄兩個關(guān)于頁面何時修改的屬性:

oldest_modification: 如果某個頁面被加載到Buffer Pool后進(jìn)行第一次修改,那么就將修改該頁面的mtr開始時對應(yīng)的lsn值寫入這個屬性。

newest_modification: 每修改一次頁面,都會將修改該頁面的mtr 結(jié)束時對應(yīng)的lsn值寫入這個屬性。也就是說該屬性表示頁面最近一次修改后對應(yīng)的系統(tǒng)lsn值。

我們接著上講的flushed_to_disk_lsn的例子看一下:

假設(shè)mtr_1執(zhí)行過程中修改了頁a ,那么在mtr_1執(zhí)行結(jié)束時,就會將頁a對應(yīng)的控制塊加入到flush鏈表的頭部。并且將mtr_1開始時對應(yīng)的lsn ,也就是8716 寫入頁a對應(yīng)的控制塊的oldest_modification屬性中,把 mtr_1結(jié)束時對應(yīng)的lsn,也就是8916寫入頁a對應(yīng)的控制塊的 newest_modification屬性中。畫個圖表示一下(為了讓美觀一些,我們把 oldest_modification 縮寫 成了o_m,把newest_modification 縮寫成了 n_m):

接著假設(shè)mtr_2執(zhí)行過程中又修改了頁b和頁c兩個頁面,那么在mtr_2執(zhí)行結(jié)束時,就會將頁b和頁c對應(yīng)的控制塊都加入到flush鏈表的頭部。并且將mtr_2開始時對應(yīng)的lsn,也就是8916寫入頁b和頁c對應(yīng)的控制塊的oldest_modification屬性中,把 mtr_2 結(jié)束時對應(yīng)的lsn,也就是9948寫入頁b和頁c對應(yīng)的控制塊的newest_modification屬性中。畫個圖表示一下:

接著假設(shè)mtr_3執(zhí)行過程中修改了頁b和頁d,不過頁b之前已經(jīng)被修改過了,所以它對應(yīng)的控制塊已經(jīng)被插入到了flush鏈表,所以在mtr_3執(zhí)行結(jié)束時,只需要將頁d對應(yīng)的控制塊都加入到flush鏈表的頭部即可。所以需要將mtr_3開始時對應(yīng)的lsn,也就是9948寫入頁d對應(yīng)的控制塊的 oldest_modification屬性中,把 mtr_3結(jié)束時對應(yīng)的lsn,也就是10000寫入頁d對應(yīng)的控制塊的newest_modification屬性中。另外,由于頁b在 mtr_3 執(zhí)行過程中又發(fā)生了一次修改,所以需要更新頁 b對應(yīng)的控制塊中newest_modification的值為10000。畫個圖表示一下:

綜上所述,flush鏈表中的臟頁按照修改發(fā)生的時間順序進(jìn)行排序,也就是按照oldest_modification代表的LSN值進(jìn)行排序,被多次更新的頁面不會重復(fù)插入到flush鏈表中,但是會更新newest_modification屬性的值。

三、checkpoint

我們要考慮一件事,就是redo日志文件組容量是有限的,我們不得不選擇循環(huán)使用redo日志文件組中的文件,但是這會造成最后寫的redo日志與最開始寫的redo日志追尾,這時應(yīng)該想到:redo日志只是為了系統(tǒng)崩潰后恢復(fù)臟頁用的,如果對應(yīng)的臟頁已經(jīng)刷新到了磁盤,也就是說即使現(xiàn)在系統(tǒng)崩潰,那么在重啟后也用不著使用redo日志恢復(fù)該頁面了,所以該redo日志也就沒有存在的必要了,那么它占用的磁盤空間就可以被后續(xù)的 redo日志所重用。也就是說:判斷某些redo日志占用的磁盤空間是否可以覆蓋的依據(jù)就是它對應(yīng)的臟頁是否已經(jīng)刷新到磁盤里。我們看一下前邊一直說的那個例子:

如圖,雖然mtr_1和mtr_2生成的redo日志都已經(jīng)被寫到了磁盤上,但是它們修改的臟頁仍然留在Buffer Pool中,所以它們生成的redo日志在磁盤上的空間是不可以被覆蓋的。之后隨著系統(tǒng)的運行,如果頁a被刷新到了磁盤,那么它對應(yīng)的控制塊就會從flush鏈表中移除,就像這樣子:

這樣mtr_1生成的redo日志就沒有用了,它們占用的磁盤空間就可以被覆蓋掉了。InnoDB提出了 一個全局變量checkpoint_lsn來代表當(dāng)前系統(tǒng)中可以被覆蓋的redo日志總量是多少,這個變量初始值也是8704 。比方說現(xiàn)在頁a被刷新到了磁盤,mtr_1生成的redo日志就可以被覆蓋了,所以我們可以進(jìn)行一個增加checkpoint_lsn的操作,我們把這個過程稱之為做一次 checkpoint 。做一次 checkpoint 其實可以分為兩個步驟:

步驟一:計算一下當(dāng)前系統(tǒng)中可以被覆蓋的redo日志對應(yīng)的lsn值最大是多少。redo 日志可以被覆蓋,意味著它對應(yīng)的臟頁被刷到了磁盤,只要我們計算出當(dāng)前系統(tǒng)中被最早修改的臟頁對應(yīng)的oldest_modification 值,那凡是在系統(tǒng)lsn值小于該節(jié)點的oldest_modification值時產(chǎn)生的redo日志都是可以被覆蓋掉的,我們就把該臟頁的oldest_modification 賦值給 checkpoint_lsn 。

比方說當(dāng)前系統(tǒng)中頁a已經(jīng)被刷新到磁盤,那么flush鏈表的尾節(jié)點就是頁c,該節(jié)點就是當(dāng)前系統(tǒng)中最早修改的臟頁了,它的oldest_modification值為8916,我們就把8916賦值給checkpoint_lsn。

步驟二:將checkpoint_lsn 和對應(yīng)的redo日志文件組偏移量以及此次 checkpint 的編號寫到日志文件的管理信息(就是checkpoint1 或者 checkpoint2 )中。InnoDB維護(hù)了一個目前系統(tǒng)做了多少次checkpoint 的變量 checkpoint_no,每做一次 checkpoint,該變量的值就加1。我們前邊說過計算一個lsn值對應(yīng)的redo日志文件組偏移量是很容易的,所以可以計算得到該checkpoint_lsn在redo日志文件組中對應(yīng)的偏移量 checkpoint_offset ,然后 把這三個值都寫到redo 日志文件組的管理信息中。

每一個redo日志文件都有2048 個字節(jié)的管理信息,但是上述關(guān)于checkpoint的信息只會被寫到日志文件組的第一個日志文件的管理信息中。不過我們是存儲到checkpoint1中還是checkpoint2中呢?InnoDB 規(guī)定,當(dāng)checkpoint_no的值是偶數(shù)時,就寫到checkpoint1中,是奇數(shù)時,就寫到checkpoint2中。記錄完checkpoint的信息之后, redo日志文件組中各個lsn值的關(guān)系就像這樣:

四、 用戶線程批量從flush鏈表中刷出臟頁

一般情況下都是后臺的線程在對LRU鏈表和flush鏈表進(jìn)行刷臟操作, 這主要因為刷臟操作比較慢,不想影響用戶線程處理請求。但是如果當(dāng)前系統(tǒng)修改頁面的操作十分頻繁,這樣就導(dǎo)致寫日志操作十分頻繁,系統(tǒng)lsn值增長過快。如果后臺的刷臟操作不能將臟頁刷出,那么系統(tǒng)無法及時做 checkpoint ,可能就需要用戶線程同步的從flush鏈表中把那些最早修改的臟頁( oldest_modification最小的臟頁)刷新到磁盤,這樣這些臟頁對應(yīng)的redo日志就沒用了,然后就可以去做checkpoint了。

五、 查看系統(tǒng)中的各種LSN值

我們可以使用SHOW ENGINE INNODB STATUS命令查看當(dāng)前 InnoDB存儲引擎中的各種LSN值的情況,比如:

mysql> SHOW ENGINE INNODB STATUS\G 
(...省略前邊的許多狀態(tài)) 
LOG --- 
Log sequence number 124476971 
Log flushed up to   124099769 
Pages flushed up to 124052503 
Last checkpoint at  124052494 
0 pending log flushes, 0 pending chkp writes 
24 log i/o's done, 2.00 log i/o's/second ---------------------- 
(...省略后邊的許多狀態(tài))

Log sequence number: 代表系統(tǒng)中的 lsn 值,也就是當(dāng)前系統(tǒng)已經(jīng)寫入的redo日志量,包括寫入log buffer中的日志。
Log flushed up to: 代表flushed_to_disk_lsn的值,也就是當(dāng)前系統(tǒng)已經(jīng)寫入磁盤的redo日志量。
Pages flushed up to: 代表 flush鏈表中被最早修改的那個頁面對應(yīng)的oldest_modification屬性值。
Last checkpoint at: 當(dāng)前系統(tǒng)的checkpoint_lsn值。

六、 innodb_flush_log_at_trx_commit的用法

為了保證事務(wù)的持久性,用戶線程在事務(wù)提交時需要將該事務(wù)執(zhí)行過程中產(chǎn)生的所有redo日志都刷新到磁盤上。這一條要求太狠了,會很明顯的降低數(shù)據(jù)庫性能。如果對事務(wù)的持久性要求不是那么強烈的話,可以選擇修改一個稱為innodb_flush_log_at_trx_commit的系統(tǒng)變量的值,該變量有3個可選的值:

0 : 當(dāng)該系統(tǒng)變量值為0時,表示在事務(wù)提交時不立即向磁盤中同步redo日志,這個任務(wù)是交給后臺線程 做的。這樣很明顯會加快請求處理速度,但是如果事務(wù)提交后服務(wù)器掛了,后臺線程沒有及時將redo日志刷新到 磁盤,那么該事務(wù)對頁面的修改會丟失。

1 : 當(dāng)該系統(tǒng)變量值為1時,表示在事務(wù)提交時需要將redo日志同步到磁盤,可以保證事務(wù)的持久性。1也是innodb_flush_log_at_trx_commit 的默認(rèn)值。

2 : 當(dāng)該系統(tǒng)變量值為2時,表示在事務(wù)提交時需要將redo日志寫到操作系統(tǒng)的緩沖區(qū)中,但并不需要保 證將日志真正的刷新到磁盤。這種情況下如果數(shù)據(jù)庫掛了,操作系統(tǒng)沒掛的話,事務(wù)的持久性還是可以保證的,但是操作系統(tǒng)也掛了的話,那就不能保證持久性了。

七、崩潰恢復(fù)

在服務(wù)器不掛的情況下,redo日志不僅沒用,反而讓性能變得更差。但是當(dāng)數(shù)據(jù)庫掛了,我們就可以在重啟時根據(jù)redo日志中的記錄就可以將頁面恢復(fù)到系統(tǒng)崩潰前的狀態(tài)。我們接下來大致看一下恢復(fù)過程。

1. 確定恢復(fù)的起點

checkpoint_lsn之前的redo日志都可以被覆蓋,也就是說這些 redo 日志對應(yīng)的臟頁都已經(jīng)被刷新到磁盤中了,既然它們已經(jīng)被刷盤,我們就沒必要恢復(fù)它們了。對于checkpoint_lsn之后的redo日志,它們對應(yīng)的臟頁可能沒被刷盤,也可能被刷盤了,我們不能確定,所以需要從checkpoint_lsn開始讀取redo日志來恢復(fù)頁面。

當(dāng)然,redo 日志文件組的第一個文件的管理信息中有兩個block都存儲了checkpoint_lsn的信息,我們當(dāng)然是要選取最近發(fā)生的那次checkpoint的信息。衡量checkpoint發(fā)生時間早晚的信息就是所謂的 checkpoint_no, 我們只要把checkpoint1和checkpoint2這兩個block中的 checkpoint_no值讀出來比一下大小,哪個checkpoint_no值更大,說明哪個block存儲的就是最近的一次checkpoint信息。這樣我們就能拿到最近發(fā)生的checkpoint 對應(yīng)的checkpoint_lsn值以及它在 redo 日志文件組中的偏移量checkpoint_offset。

2. 確定恢復(fù)的終點

redo日志恢復(fù)的起點確定了,那終點是哪個呢?這個還得從block的結(jié)構(gòu)說起。我們說在寫redo日志的時候都是順序?qū)懙?,寫滿了一個block之后會再往下一個block中寫:

普通block的 log block header部分有一個稱之為 LOG_BLOCK_HDR_DATA_LEN的屬性,該屬性值記錄了當(dāng)前block里使用了多少字節(jié)的空間。對于被填滿的block來說,該值永遠(yuǎn)為512 。如果該屬性的值不為512,那么它就是此次崩潰恢復(fù)中需要掃描的最后一個block。

3. 怎么恢復(fù)

確定了需要掃描哪些redo 日志進(jìn)行崩潰恢復(fù)之后,接下來就是怎么進(jìn)行恢復(fù)了。假設(shè)現(xiàn)在的redo日志文件中有5條redo日志,如圖:

由于redo 0在checkpoint_lsn后邊,恢復(fù)時可以不管它。我們現(xiàn)在可以按照redo日志的順序依次掃描checkpoint_lsn之后的各條redo日志,按照日志中記載的內(nèi)容將對應(yīng)的頁面恢復(fù)出來。這樣沒什么問題,不過InnoDB還是想了一些辦法加快這個恢復(fù)的過程:

使用哈希表: 根據(jù)redo日志的space ID和page number屬性計算出散列值,把space ID和page number相同的redo日志放到哈希表的同一個槽里,如果有多個space ID和page number都相同的redo日志,那么它們之間使用鏈表連接起來,按照生成的先后順序鏈接起來的,如圖所示:

之后就可以遍歷哈希表,因為對同一個頁面進(jìn)行修改的redo日志都放在了一個槽里,所以可以一次性將一個頁面修復(fù)好,這樣可以加快恢復(fù)速度。

另外需要注意一點的是,同一個頁面的redo日志是按照生成時間順序進(jìn)行排序的,所以恢復(fù)的時候也是按照這個順序進(jìn)行恢復(fù),如果不按照生成時間順序進(jìn)行排序的話,那么可能出現(xiàn)錯誤。比如原先的修改操作是先插入一條記錄,再刪除該條記錄,如果恢復(fù)時不按照這個順序來,就可能變成先刪除一條記錄,再插入一條記錄,這顯然是錯誤的。

跳過已經(jīng)刷新到磁盤的頁面: checkpoint_lsn之前的redo日志對應(yīng)的臟頁確定都已經(jīng)刷到磁盤了,但是 checkpoint_lsn之后的redo日志我們不能確定是否已經(jīng)刷到磁盤,主要是因為在最近做的一次checkpoint后,可能后臺線程又不斷的從LRU鏈表和flush鏈表中將一些臟頁刷出Buffer Pool 。這些 在checkpoint_lsn之后的redo日志,如果它們對應(yīng)的臟頁在崩潰發(fā)生時已經(jīng)刷新到磁盤,那在恢復(fù)時也就沒有必要根據(jù)redo 日志的內(nèi)容修改該頁面了。

那在恢復(fù)時怎么知道某個redo日志對應(yīng)的臟頁是否在崩潰發(fā)生時已經(jīng)刷新到磁盤了呢?這還得從頁面的結(jié)構(gòu)說起,我們知道每個頁面都有一個稱之為File Header的部分,在File Header里有一個稱之為 FIL_PAGE_LSN的屬性,該屬性記載了最近一次修改頁面時對應(yīng)的lsn值(其實就是頁面控制塊中的newest_modification值)。如果在做了某次 checkpoint之后有臟頁被刷新到磁盤中,那么該頁對應(yīng)的FIL_PAGE_LSN 代表的lsn值肯定大于checkpoint_lsn的值,凡是符合這種情況的頁面就不需要重復(fù)執(zhí)行 lsn值小于 FIL_PAGE_LSN 的redo日志了,所以更進(jìn)一步提升了崩潰恢復(fù)的速度。

八、 遺漏的問題:LOG_BLOCK_HDR_NO是如何計算的

我們前邊說過,對于實際存儲redo日志的普通的log block來說,在 log block header處有一個稱之為LOG_BLOCK_HDR_NO的屬性,我們說這個屬性代表一個唯一的標(biāo)號。這個屬性是初次使用該block時分配的,跟當(dāng)時的系統(tǒng)lsn值有關(guān)。使用下邊的公式計算該block的 LOG_BLOCK_HDR_NO值:

((lsn / 512) & 0x3FFFFFFFUL) + 1

這個公式里的0x3FFFFFFFUL可能讓大家有點困惑,其實它的二進(jìn)制表示可能更親切一點:

從圖中可以看出,0x3FFFFFFFUL對應(yīng)的二進(jìn)制數(shù)的前2位為0,后30位的值都為1 。一個二進(jìn)制位與0做與運算(&)的結(jié)果肯定是0,一個二進(jìn)制位與1做與運算(&)的結(jié)果就是原值。讓一個數(shù)和0x3FFFFFFFUL 做與運算的意思就是要將該值的前2個比特位的值置為0,這樣該值就肯定小于或等于 0x3FFFFFFFUL了。

這也就說明了,不論lsn多大, ((lsn / 512) & 0x3FFFFFFFUL) 的值肯定在0到0x3FFFFFFFUL之間,再加1的話肯定在1到0x40000000UL之間。而0x40000000UL這個值就代表著1GB。也就是說系統(tǒng)最多能產(chǎn)生不重復(fù)的LOG_BLOCK_HDR_NO值只有1GB個。InnoDB規(guī)定redo日志文件組中包含的所有文件大小總和不得超過512GB,一個block大小是512字節(jié),也就是說redo日志文件組中包含的block塊最多為1GB個,所以有1GB個不重復(fù)的編號值也就夠用了。

另外,LOG_BLOCK_HDR_NO值的第一個比特位比較特殊,稱之為 flush bit,如果該值為1,代表著本block是在某次將log buffer中的block刷新到磁盤的操作中的第一個被刷入的block。

到此這篇關(guān)于mysql中 redo日志的文章就介紹到這了,更多相關(guān)mysql redo日志內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入解析MySQL索引數(shù)據(jù)結(jié)構(gòu)

    深入解析MySQL索引數(shù)據(jù)結(jié)構(gòu)

    什么是索引?索引就是排好序的數(shù)據(jù)結(jié)構(gòu),可以幫助我們快速的查找到數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于MySQL索引數(shù)據(jù)結(jié)構(gòu)的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • MySQL聯(lián)合索引與最左匹配原則的實現(xiàn)

    MySQL聯(lián)合索引與最左匹配原則的實現(xiàn)

    最左匹配原則在我們MySQL開發(fā)過程中和面試過程中經(jīng)常遇到,為了加深印象和理解,我在這里把MySQL的最左匹配原則詳細(xì)的講解一下,感興趣的可以了解一下
    2023-12-12
  • 用SELECT... INTO OUTFILE語句導(dǎo)出MySQL數(shù)據(jù)的教程

    用SELECT... INTO OUTFILE語句導(dǎo)出MySQL數(shù)據(jù)的教程

    這篇文章主要介紹了用SELECT... INTO OUTFILE語句導(dǎo)出MySQL數(shù)據(jù)的教程,是MySQL入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-05-05
  • MSSQL產(chǎn)生死鎖的根本原因及解決方法

    MSSQL產(chǎn)生死鎖的根本原因及解決方法

    死鎖是指兩個或兩個以上的進(jìn)程在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去.此時稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等的進(jìn)程稱為死鎖進(jìn)程
    2016-04-04
  • 總結(jié)三道MySQL聯(lián)合索引面試題

    總結(jié)三道MySQL聯(lián)合索引面試題

    這篇文章主要介紹了總結(jié)三道MySQL聯(lián)合索引面試題,眾所周知MySQL聯(lián)合索引遵循最左前綴匹配原則,在少數(shù)情況下也會不遵循,創(chuàng)建聯(lián)合索引的時候,建議優(yōu)先把區(qū)分度高的字段放在第一列
    2022-08-08
  • MLSQL編譯時權(quán)限控制示例詳解

    MLSQL編譯時權(quán)限控制示例詳解

    這篇文章主要給大家介紹了關(guān)于MLSQL編譯時權(quán)限控制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用mysql具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • MySQL性能優(yōu)化之table_cache配置參數(shù)淺析

    MySQL性能優(yōu)化之table_cache配置參數(shù)淺析

    這篇文章主要介紹了MySQL性能優(yōu)化之table_cache配置參數(shù)淺析,本文介紹了它的緩存機制、參數(shù)優(yōu)化及清空緩存的命令等,需要的朋友可以參考下
    2014-07-07
  • MySQL內(nèi)存表的特性與使用介紹

    MySQL內(nèi)存表的特性與使用介紹

    臨時表和內(nèi)存表都可以人工創(chuàng)建,但臨時表更多的作用是系統(tǒng)自己創(chuàng)建后,組織數(shù)據(jù)以提升性能,如子查詢,臨時表在多個連接之間不能共享。這里只討論內(nèi)存表
    2013-02-02
  • Mysql?optimize?table?時報錯:Temporary?file?write?fail的解決

    Mysql?optimize?table?時報錯:Temporary?file?write?fail的解決

    這篇文章主要介紹了Mysql?optimize?table?時報錯:Temporary?file?write?fail的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 將json文件數(shù)據(jù)導(dǎo)入到MySQL表中的詳細(xì)教程

    將json文件數(shù)據(jù)導(dǎo)入到MySQL表中的詳細(xì)教程

    如何使用json文件將數(shù)據(jù)導(dǎo)入到MySQL數(shù)據(jù)庫中的表里?Excel表格等文件的數(shù)據(jù)通過java或者python等語言讀取后生成一個json文件,然后想要將文件中的數(shù)據(jù)寫入到MySQL表中,本文介紹了將json文件數(shù)據(jù)導(dǎo)入到MySQL表中的詳細(xì)教程,需要的朋友可以參考下
    2024-07-07

最新評論