MySQL redo日志寫入磁盤的實現(xiàn)過程
1、背景
之前我們講過redo日志類型有很多種,但是要想保證服務器崩潰數(shù)據(jù)能還原,這些日志還是得存儲在磁盤上,接下來我們就來講解一下redo日志的存儲過程。
2、redo日志存儲過程
【1】redo log block
redo日志也是存儲在頁中的,頁的大小為512字節(jié),存儲redo日志的頁就被稱為redo log block,一個redo log block有三部分組成,其組成如圖:
字段含義如下:
字段名 | 字節(jié)大小 | 含義 |
---|---|---|
log block header | 12 | 由4部分組成,存儲管理信息 |
log block body | 496 | 真正存儲redo日志數(shù)據(jù)的地方 |
log block trailer | 4 | 由1部分組成,用于校驗正確性 |
log block header的組成結(jié)果如圖:
字段含義如下:
字段名 | 字節(jié)大小 | 含義 |
---|---|---|
LOG_BLOCK_HDR_NO | 4 | 每個block的唯一編號 |
LOG_BLOCK_HDR_DATA_LEN | 2 | block中已經(jīng)使用了多少字節(jié),初始就為log block header大小12 |
LOG_BLOCK_FIRST_REC_GROUP | 2 | 這個block中第有個mtr(Mini-Transaction)對應的redo日志組中第一條redo日志偏移量 |
LOG_BLOCK_CHECKPOINT_NO | 4 | checkpoint序號 |
【2】log buffer
redo日志也不是直接寫入磁盤,會寫入一塊連續(xù)的內(nèi)存區(qū)域,這塊連續(xù)的內(nèi)存就叫log buffer,其結(jié)構(gòu)如圖:
這塊連續(xù)內(nèi)存的大小可以通過innodb_log_buffer_size字段查看,單位字節(jié):
mysql> show VARIABLES LIKE 'innodb_log_buffer_size'; +------------------------+----------+ | Variable_name | Value | +------------------------+----------+ | innodb_log_buffer_size | 16777216 | +------------------------+----------+ 1 row in set, 1 warning (0.00 sec)
【3】buf_free
一個事務可以有多個mtr,一個mtr可以有多個redo日志記錄,將redo日志寫入log buffer中時,時間上是寫入block中的log block body部分,當一個block寫滿也就是log block body寫滿時,就去寫連續(xù)的下一個block,可以寫入redo日志的空閑log buffer的最小偏移量就叫buf_free,所以每次我們要寫入redo日志只需要找到buf_free偏移量,就可以知道在log buffer的哪個地方寫redo日志了,可以如圖表示:
【4】redo日志刷盤時機
log buffer空閑有限,所以也需要將redo日志寫到磁盤再釋放空間,用于寫入新的redo日志,從內(nèi)存刷到磁盤的時機有如下幾種:
- 1、log buffer空間不足
- 2、事務提交時
- 3、后臺線程定時刷
- 4、正常關閉服務器
- 5、checkpoint時
【5】redo日志文件組
從log buffer刷新到磁盤中的redo日志是存儲在ib_logfile0、ib_logfile1、ib_logfilen文件中,這些文件就叫redo日志文件組,可以通過配置文件指定此組文件的目錄、大小、數(shù)量。
查看目錄方式如下:
mysql> show VARIABLES LIKE 'innodb_log_group_home_dir'; +---------------------------+--------------------------------------------+ | Variable_name | Value | +---------------------------+--------------------------------------------+ | innodb_log_group_home_dir | /xxx/data | +---------------------------+--------------------------------------------+ 1 row in set (0.002 sec)
查看大小方式如下,單位字節(jié):
mysql> show VARIABLES LIKE 'innodb_log_file_size'; +----------------------+----------+ | Variable_name | Value | +----------------------+----------+ | innodb_log_file_size | 50331648 | +----------------------+----------+ 1 row in set, 1 warning (0.01 sec)
查看數(shù)量方式如下:
mysql> show VARIABLES LIKE 'innodb_log_files_in_group'; +---------------------------+-------+ | Variable_name | Value | +---------------------------+-------+ | innodb_log_files_in_group | 2 | +---------------------------+-------+ 1 row in set, 1 warning (0.00 sec)
查看一下ib_logfile文件如下:
[root@xxx xxx]# ls /xxx/data/ | grep 'ib_log' ib_logfile0 ib_logfile1
寫文件是ib_logfile0到ib_logfile1循環(huán)寫入,當ib_logfile1寫滿時就重新從ib_logfile0重新寫入。
【6】ib_logfile文件格式
和log buffer的組成結(jié)構(gòu)一樣,ib_logfile也是由連續(xù)的512字節(jié)大小的block組成,有區(qū)別的是ib_logfile的前四個block用了存儲管理信息,后面的block才用來存儲redo日志,其存儲過程可以如圖表示:
redo日志文件前四個block分別為log file header、checkpoint1、未使用、checkpoint2,checkpoint1和checkpoint2是一樣的,接下來我們就來講一下log file header和checkpoint。
log file header結(jié)構(gòu)如下:
其字段含義為:
字段 | 字節(jié)大小 | 含義 |
---|---|---|
LOG_HEADER_FORMAT | 4 | redo日志版本 |
LOG_HEADER_PAD1 | 4 | 無用 |
LOG_HEADER_START_LSN | 8 | 標記redo日志文件開始的LSN值,也就是第5個block開始對應的LSN值 |
LOG_HEADER_CREATOR | 32 | 標記redo日志的創(chuàng)建者 |
LOG_BLOCK_CHECKSUM | 4 | 本block的校驗值 |
checkpoint結(jié)構(gòu)如下:
其字段含義為:
字段 | 字節(jié)大小 | 含義 |
---|---|---|
LOG_CHECKPOINT_NO | 8 | 服務器每做一次checkpoint,該值就加1 |
LOG_CHECKPOINT_LSN | 8 | 服務器做checkpoint結(jié)束時對應的LSN值,系統(tǒng)崩潰恢復時從該值開始 |
LOG_CHECKPOINT_OFFSET | 8 | 上個屬性中LSN值在redo日志文件組中的偏移量 |
LOG_CHECKPOINT_LOG_BUF_SIZE | 8 | 服務器在做checkpoint時對應的log buffer大小 |
LOG_BLOCK_CHECKSUM | 4 | block的校驗值 |
【7】Log Sequeue Number
log buffer中,InnoDB為已經(jīng)寫入的redo日志量用個全局變量Log Sequeue Number表示,也叫日志序列號,簡稱lsn,lsn的初始值為8704,但是block真正寫redo日志的是中間的body部分,所以初始的lsn值需要加上第一個block的log block header(12字節(jié))的大小得到初始的lsn值為8716,需要注意后續(xù)寫入的redo日志如果占用了幾個block,計算lsn就需要加上log block header和log block trailer。
【8】flushed_to_disk_lsn
lsn是標記log buffer中已經(jīng)寫入redo日志的量,不關心log buffer上的redo日志是否刷新到磁盤上也就是ib_logfile中,所以InnoDB中提供了flushed_to_disk_lsn的全局變量來標識log buffer中已經(jīng)被刷到磁盤的量。
【9】flush鏈表中的lsn
當我們修改一條數(shù)據(jù)可能會產(chǎn)生以下流程,首先會產(chǎn)生多個mtr,將這些mtr對應的redo日志組寫到log buffer中,然后修改buffer pool中的頁,最后再將修改的頁對應的控制塊加入flush鏈表,控制塊中有個兩個記錄lsn的屬性,oldest_modification和newest_modification,其屬性含義如下:
屬性名 | 含義 |
---|---|
oldest_modification | 控制塊對應的頁被修改起始對應的lsn值 |
newest_modification | 控制塊對應的頁被修改結(jié)束對應的lsn值 |
需要注意的是多個mtr修改相同的頁,不會修改頁對應的控制塊在鏈表中的位置,但是會更新newest_modification屬性的值。
【10】checkpoint
InnoDB中提出了一個全局變量checkpoint_lsn,用來表示redo日志文件中小于該值的部分都可以被覆蓋,checkpoint指的是buffer pool中的臟頁被刷新到磁盤,這時臟頁對應的redo日志就可以被覆蓋了,我們就能計算一個checkpoint_lsn值這個過程。
InnoDB中還有一個checkpoin_no的變量,每執(zhí)行一次checkpoint,該值就會加1。
前面講過checkpoint的屬性就會包含:checkpoint_lsn、checkpoint_no、checkpoint_lsn對應的偏移量,至于這些屬性是存儲在checkpoint1還是checkpoint2中,是根據(jù)checkpoint_no是奇數(shù)還是偶數(shù),如果是奇數(shù)就寫到checkpoit2中,偶數(shù)就寫checkpoint1中。
【11】系統(tǒng)中的LSN值
我們可以查看系統(tǒng)中的lsn值,可以通過如下命令去查看:
mysql> show engine innodb status\G; ... --- LOG --- Log sequence number 20993334 Log buffer assigned up to 20993334 Log buffer completed up to 20993334 Log written up to 20993334 Log flushed up to 20993334 Added dirty pages up to 20993334 Pages flushed up to 20993334 Last checkpoint at 20993334 Log minimum file id is 6 Log maximum file id is 6 25 log i/o's done, 0.00 log i/o's/second ...
其字段含義為:
字段名 | 含義 |
---|---|
Log sequence number | 當前redo日志中LSN的最大值,表示所有修改數(shù)據(jù)的操作產(chǎn)生的日志序列號,該值會隨著日志的寫入逐漸增大 |
Log buffer assigned up to | log buffer中被分配到的lsn值,與Log sequence number相同 |
Log buffer completed up to | log buffer中準備好寫磁盤的lsn值,與Log sequence number相同 |
Log written up to | 已經(jīng)寫到redo日志文件的日志序列號 |
Log flushed up to | 已經(jīng)刷新到磁盤的redo日志序列號 |
Added dirty pages up to | 已添加到臟頁列表的頁面的lsn值 |
Pages flushed up to | 已經(jīng)刷新到磁盤臟頁的lsn值 |
Last checkpoint at | 當前checkpoit_lsn的值 |
Log minimum file id is | redo日志文件的最小文件ID |
Log maximum file id is | redo日志文件的最大文件ID |
【12】innodb_flush_log_at_trx_commit
redo日志刷新到磁盤的方式由innodb_flush_log_at_trx_commit控制,刷新方式有3種,分別為:
innodb_flush_log_at_trx_commit值 | 刷新方式 |
---|---|
0 | 事務提交時不立即刷新redo日志到磁盤,由后臺線程去刷新到磁盤,如果服務器掛了,后臺線程沒及時將redo日志刷新到磁盤,事務對該頁面的修改就會丟失 |
1 | 事務提交時就將redo日志同步到磁盤 |
2 | 事務提交時將redo日志寫入log buffer,不需要刷新到磁盤,當數(shù)據(jù)庫掛,操作系統(tǒng)掛也能保證持久性,數(shù)據(jù)庫和操作系統(tǒng)都掛就保證不了持久性了 |
【13】崩壞是redo日志恢復
redo日志的恢復過程如下:
1、確定恢復的起點,也就是checkpoint_lsn,
2、確定恢復的終點,也就是ib_logfile中block未寫滿的位置,
3、從checkpoint_lsn恢復頁時,如果頁的FIL_PAGE_LSN值,也就是newest_modification的值如果大于checkpoint_lsn,說明該頁已經(jīng)恢復,直接跳過,如果小于checkpoint_lsn,就需要讀取redo日志進行恢復。
3、總結(jié)
redo日志是InnoDB使用崩潰恢復的核心機制,通過物理日志、順序?qū)懭牒蚦heckpoint機制,保證事務的持久性并且提高了數(shù)據(jù)庫的性能。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
使用mss2sql工具將SqlServer轉(zhuǎn)換為Mysql全記錄
上篇文章我們講訴了在mssql數(shù)據(jù)轉(zhuǎn)換成mysql數(shù)據(jù)中,用Navicat Premium導入數(shù)據(jù)很完美,但是創(chuàng)建表的時候數(shù)據(jù)類型轉(zhuǎn)換不是很完美,本文我們來講訴下用mss2sql工具來創(chuàng)建表,順便說下導入數(shù)據(jù)2014-08-08解決找回mysql數(shù)據(jù)庫密碼和密碼過期問題
這篇文章主要介紹了解決找回mysql數(shù)據(jù)庫密碼和密碼過期問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06