具體細(xì)節(jié) 請(qǐng)去掘金購(gòu)買《MySQL 是怎樣運(yùn)行的:從根兒上理解 MySQL》
redo日志刷盤時(shí)機(jī)
- 1.當(dāng)MTR產(chǎn)生的一組redo日志在mtr結(jié)束的時(shí)候會(huì)被復(fù)制到log buffer里面
- 2.mtr是在對(duì)應(yīng)的語(yǔ)句執(zhí)行完畢時(shí)候結(jié)束
- 3.log buffer刷入磁盤的時(shí)機(jī)分為以下幾種:
4.log buffer 空間不足
- 1.通過(guò)系統(tǒng)變量innodb_log_buffer_size指定 logbuffer的大小
- 2.當(dāng)redo日質(zhì)量占滿了log buffer一半就需要把日志刷新到磁盤上(可能會(huì)存在事務(wù)未提交恤左,但是也被刷新到磁盤)
5.事務(wù)提交
- 1.在事務(wù)提交的時(shí)候需要同步把log buffer刷新到磁盤飞袋,才能保證事務(wù)的持久性
6.后臺(tái)線程不停的刷
- 1.后臺(tái)還有線程,每秒刷新一次log buffer中的日志到磁盤
7.正常關(guān)閉服務(wù)器的時(shí)候
8.check point時(shí)候
redo日志文件組
- 1.在mysql的數(shù)據(jù)目錄下默認(rèn)有ib_logfile0和ib_logfile1的文件物咳,這就是對(duì)應(yīng)的redo日志文件
- 2.通過(guò)系統(tǒng)變量innodb_log_group_home_dir指定了redo日志文件所在的目錄
- 3.通過(guò)系統(tǒng)變量innodb_log_file_size指定了每個(gè)redo日志文件的大小蹄皱,在MySQL 5.7.21這個(gè)版本中的默認(rèn)值為48MB
- 4.通過(guò)系統(tǒng)變量innodb_log_files_in_group指定redo日志文件的個(gè)數(shù),默認(rèn)值為2压鉴,最大值為100锻拘。
- 5.總共的redo日志文件大小其實(shí)就是:innodb_log_file_size × innodb_log_files_in_group。
- 6.redo日志文件組婉宰,都是循環(huán)寫入推穷,一個(gè)文件寫滿了就寫下一個(gè),寫到結(jié)尾繼續(xù)循環(huán)(從redo日志文件的第2048個(gè)字節(jié)開(kāi)始)寫入(當(dāng)然會(huì)把一些事物修改的結(jié)果落入數(shù)據(jù)頁(yè)才可以覆蓋)
redo日志文件格式
- 1.redo日志文件和redo日志分配的block大小相同
- 2.redo日志文件組的每個(gè)文件大小都一樣馒铃,格式也一樣都是由下面兩部分組成
- 3.前2048個(gè)字節(jié)(也就是4個(gè)block)用來(lái)存在一些管理信息的
- 4.從第2048個(gè)字節(jié)往后是用來(lái)存儲(chǔ)logbuffer中的block
- 5.所以每次循環(huán)使用是從2048個(gè)字節(jié)開(kāi)始
redo日志文件的前四個(gè)block
- 1.log file header:描述該redo日志文件的一些整體屬性
- 2.checkpoint1:記錄關(guān)于checkpoint的一些屬性
- 3.沒(méi)用
- 4.checkpoint2
log file header
- 1.LOG_HEADER_FORMAT: redo日志的版本区宇,在MySQL 5.7.21中該值永遠(yuǎn)為1
- 2.LOG_HEADER_START_LSN:標(biāo)記本redo日志文件開(kāi)始的LSN值,也就是文件偏移量為2048字節(jié)初對(duì)應(yīng)的LSN值
- 3.LOG_HEADER_CREATOR:一個(gè)字符串炉爆,標(biāo)記本redo日志文件的創(chuàng)建者是誰(shuí)。正常運(yùn)行時(shí)該值為MySQL的版本號(hào)
- 4.LOG_BLOCK_CHECKSUM:本block的校驗(yàn)值
- 5.LOG_HEADER_PAD1:做字節(jié)填充用的
checkpoint1和checkpoint2的屬性結(jié)構(gòu)
- 1.LOG_CHECKPOINT_NO:服務(wù)器做checkpoint的編號(hào)芬首,每做一次checkpoint,該值就加1
- 2.LOG_CHECKPOINT_LSN:服務(wù)器做checkpoint結(jié)束時(shí)對(duì)應(yīng)的LSN值,系統(tǒng)奔潰恢復(fù)時(shí)將從該值開(kāi)始
- 3.LOG_CHECKPOINT_OFFSET:上個(gè)屬性中的LSN值在redo日志文件組中的偏移量
- 4.LOG_CHECKPOINT_LOG_BUF_SIZE: 服務(wù)器在做checkpoint操作時(shí)對(duì)應(yīng)的log buffer的大小
- 5.LOG_BLOCK_CHECKSUM:本block的校驗(yàn)值
Log Sequeue Number(日志序列號(hào))
- 1.InnoDB為記錄已經(jīng)寫入的redo日志量艺晴,設(shè)計(jì)了一個(gè)稱之為L(zhǎng)og Sequeue Number的全局變量
- 2.LSN的初始值為8704
- 3.統(tǒng)計(jì)log buffer寫入到redo日志的量,不僅僅包含日志本身封寞,還包含log block header和log block trailer
- 4.每一組由mtr生成的redo日志都有一個(gè)唯一的LSN值與其對(duì)應(yīng)仅财,LSN值越小,說(shuō)明redo日志產(chǎn)生的越早
flushed_to_disk_lsn
- 1.全局變量buf_next_to_write標(biāo)志著log buffer 中已經(jīng)有哪些日志被刷新到磁盤中
- 2.flushed_to_disk_lsn代表刷新到磁盤中的redo日志量的全局變量--該變量的值和初始的lsn值是相同的抖锥,都是8704
- 3.如果LSN和flushed_to_disk_lsn如果兩者的值相同時(shí)碎罚,說(shuō)明log buffer中的所有redo日志都已經(jīng)刷新到磁盤中了。
- 4.其實(shí)只有當(dāng)系統(tǒng)執(zhí)行了fsync函數(shù)后荆烈,flushed_to_disk_lsn的值才會(huì)跟著增長(zhǎng),當(dāng)僅僅把log buffer中的日志寫入到操作系統(tǒng)緩沖區(qū)卻沒(méi)有顯式的刷新到磁盤時(shí)宫峦,另外的一個(gè)稱之為write_lsn的值跟著增長(zhǎng)
lsn值和redo日志文件偏移量的對(duì)應(yīng)關(guān)系
flush鏈表中的LSN
- 1.在mtr結(jié)束時(shí),把在mtr執(zhí)行過(guò)程中可能修改過(guò)的頁(yè)面加入到Buffer Pool的flush鏈表
- 2.flush鏈表中的臟頁(yè)是按照頁(yè)面的第一次修改時(shí)間從大到小進(jìn)行排序的
- 3.緩存頁(yè)對(duì)應(yīng)的控制塊中記錄兩個(gè)關(guān)于頁(yè)面何時(shí)修改的屬性:oldest_modification和newest_modification
- 4.oldest_modification:如果某個(gè)頁(yè)面被加載到Buffer Pool后進(jìn)行第一次修改导绷,那么就將修改該頁(yè)面的mtr開(kāi)始時(shí)對(duì)應(yīng)的lsn值寫入這個(gè)屬性
- 5.newest_modification:每修改一次頁(yè)面屎飘,都會(huì)將修改該頁(yè)面的mtr結(jié)束時(shí)對(duì)應(yīng)的lsn值寫入這個(gè)屬性。也就是說(shuō)該屬性表示頁(yè)面最近一次修改后對(duì)應(yīng)的系統(tǒng)lsn值
- 6.flush鏈表中的臟頁(yè)按照修改發(fā)生的時(shí)間順序進(jìn)行排序枚碗,也就是按照oldest_modification代表的LSN值進(jìn)行排序,被多次更新的頁(yè)面不會(huì)重復(fù)插入到flush鏈表中遵堵,但是會(huì)更新newest_modification屬性的值
checkpoint
- 1.判斷某些redo日志占用的磁盤空間是否可以覆蓋的依據(jù)就是它對(duì)應(yīng)的臟頁(yè)是否已經(jīng)刷新到磁盤里
- 2.checkpoint_lsn:代表當(dāng)前系統(tǒng)中可以被覆蓋的redo日志總量是多少,這個(gè)變量初始值也是8704锡足。
- 3.增加checkpoint_lsn就是叫checkpoint
步驟一:計(jì)算一下當(dāng)前系統(tǒng)中可以被覆蓋的redo日志對(duì)應(yīng)的lsn值最大是多少壳坪。
- 1.尋找最早修改的臟頁(yè)對(duì)應(yīng)的oldest_modification--只要查看flush鏈表最后一個(gè)頁(yè)面的oldest_modification
- 2.那凡是在系統(tǒng)lsn值小于該節(jié)點(diǎn)的oldest_modification值時(shí)產(chǎn)生的redo日志都是可以被覆蓋掉的
- 3.我們就把該臟頁(yè)的oldest_modification賦值給checkpoint_lsn。
步驟二:將checkpoint_lsn和對(duì)應(yīng)的redo日志文件組偏移量以及此次checkpint的編號(hào)寫到日志文件的管理信息(就是checkpoint1或者checkpoint2)中沐批。
- 1.InnoDB維護(hù)了一個(gè)目前系統(tǒng)做了多少次checkpoint的變量checkpoint_no
- 2.關(guān)于checkpoint的信息只會(huì)被寫到日志文件組的第一個(gè)日志文件的管理信息中
- 3.當(dāng)checkpoint_no的值是偶數(shù)時(shí)蝎亚,就寫到checkpoint1中,是奇數(shù)時(shí)躺彬,就寫到checkpoint2中
批量從flush鏈表中刷出臟頁(yè)
- 1.一般情況下都是后臺(tái)的線程在對(duì)LRU鏈表和flush鏈表進(jìn)行刷臟操作
- 2.如果后臺(tái)的刷臟操作不能將臟頁(yè)刷出梅惯,那么系統(tǒng)無(wú)法及時(shí)做checkpoint,可能就需要用戶線程同步的從flush鏈表中把那些最早修改的臟頁(yè)(oldest_modification最小的臟頁(yè))刷新到磁
- 3.上述情況導(dǎo)致這些臟頁(yè)對(duì)應(yīng)的redo日志就沒(méi)用了江解,然后就可以去做checkpoint了徙歼。
查看系統(tǒng)中的各種LSN值
- 1.使用SHOW ENGINE INNODB STATUS命令查看當(dāng)前InnoDB存儲(chǔ)引擎中的各種LSN值的情況
- 2.Log sequence number:代表系統(tǒng)中的lsn值,也就是當(dāng)前系統(tǒng)已經(jīng)寫入的redo日志量桨螺,包括寫入log buffer中的日志
- 3.Log flushed up to:代表flushed_to_disk_lsn的值酿秸,也就是當(dāng)前系統(tǒng)已經(jīng)寫入磁盤的redo日志量
- 4.Pages flushed up to:代表flush鏈表中被最早修改的那個(gè)頁(yè)面對(duì)應(yīng)的oldest_modification屬性值
- 5.Last checkpoint at:當(dāng)前系統(tǒng)的checkpoint_lsn值。
innodb_flush_log_at_trx_commit
- 1.為了保證事務(wù)的持久性辣苏,用戶線程在事務(wù)提交時(shí)需要將該事務(wù)執(zhí)行過(guò)程中產(chǎn)生的所有redo日志都刷新到磁盤上
- 2.但是1會(huì)降低數(shù)據(jù)庫(kù)性能,如果對(duì)事務(wù)的持久性要求不是那么清冽可以修改innodb_flush_log_at_trx_commit
- 3.innodb_flush_log_at_trx_commit=0:當(dāng)該系統(tǒng)變量值為0時(shí)煌张,表示在事務(wù)提交時(shí)不立即向磁盤中同步redo日志退客,這個(gè)任務(wù)是交給后臺(tái)線程做的
- 4.innodb_flush_log_at_trx_commit=1(默認(rèn)):表示在事務(wù)提交時(shí)需要將redo日志同步到磁盤链嘀,可以保證事務(wù)的持久性
- 5.innodb_flush_log_at_trx_commit=2:表示在事務(wù)提交時(shí)需要將redo日志寫到操作系統(tǒng)的緩沖區(qū)中怀泊,但并不需要保證將日志真正的刷新到磁盤误趴,需要OS自己控制
崩潰恢復(fù)
確定恢復(fù)的起點(diǎn)
- 1.checkpoint_lsn之后的都是(有可能)未刷入磁盤的,所以需要解析這部分redo日志
- 2.redo日志文件組的第一個(gè)文件的管理信息中有兩個(gè)block都存儲(chǔ)了checkpoint_lsn的信息凉当,我們當(dāng)然是要選取最近發(fā)生的那次checkpoint的信息--通過(guò)checkpoint_no比較
- 3.這樣我們就能拿到最近發(fā)生的checkpoint對(duì)應(yīng)的checkpoint_lsn值以及它在redo日志文件組中的偏移量checkpoint_offset。
確定恢復(fù)的終點(diǎn)
- 1.普通block的log block header部分有一個(gè)稱之為L(zhǎng)OG_BLOCK_HDR_DATA_LEN的屬性纤怒,該屬性值記錄了當(dāng)前block里使用了多少字節(jié)的空間
- 2.對(duì)于被填滿的block來(lái)說(shuō)泊窘,該值永遠(yuǎn)為512像寒。如果該屬性的值不為512,那么就是它了诺祸,它就是此次奔潰恢復(fù)中需要掃描的最后一個(gè)block。
怎么恢復(fù)
- 1.使用哈希表:根據(jù)redo日志的space ID和page number屬性計(jì)算出散列值憔鬼,把space ID和page number相同的redo日志放到哈希表的同一個(gè)槽里
胃夏,如果有多個(gè)space ID和page number都相同的redo日志,那么它們之間使用鏈表連接起來(lái)照雁,按照生成的先后順序鏈接起來(lái)的 - 2.之后就可以遍歷哈希表答恶,因?yàn)閷?duì)同一個(gè)頁(yè)面進(jìn)行修改的redo日志都放在了一個(gè)槽里,所以可以一次性將一個(gè)頁(yè)面修復(fù)好(避免了很多讀取頁(yè)面的隨機(jī)IO)污呼,這樣可以加快恢復(fù)速度
- 3.另外需要注意一點(diǎn)的是,同一個(gè)頁(yè)面的redo日志是按照生成時(shí)間順序進(jìn)行排序的曙求,所以恢復(fù)的時(shí)候也是按照這個(gè)順序進(jìn)行恢復(fù),如果不按照生成時(shí)間順序進(jìn)行排序的話静浴,那么可能出現(xiàn)錯(cuò)誤挤渐。比如原先的修改操作是先插入一條記錄,再刪除該條記錄
得问,如果恢復(fù)時(shí)不按照這個(gè)順序來(lái)软免,就可能變成先刪除一條記錄,再插入一條記錄漓骚,這顯然是錯(cuò)誤的 - 4.跳過(guò)已經(jīng)刷新到磁盤的頁(yè)面:因?yàn)閏heckpoint_lsn之后的redo日志我們不能確定是否已經(jīng)刷到磁盤
- 5.通過(guò)頁(yè)面的File Header的FIL_PAGE_LSN屬性:記載了最近一次修改頁(yè)面時(shí)對(duì)應(yīng)的lsn值(其實(shí)就是頁(yè)面控制塊中的newest_modification值)
- 6.如果在做了某次checkpoint之后有臟頁(yè)被刷新到磁盤中榛泛,那么該頁(yè)對(duì)應(yīng)的FIL_PAGE_LSN代表的lsn值肯定大于checkpoint_lsn的值,凡是符合這種情況的頁(yè)面就不需要重復(fù)執(zhí)行l(wèi)sn值小于FIL_PAGE_LSN的redo日志了
恢復(fù)策略
- 1.前面說(shuō)到未提交的事務(wù)和回滾了的事務(wù)也會(huì)記錄Redo Log孤个,因此在進(jìn)行恢復(fù)時(shí),這些事務(wù)要進(jìn)行特殊的
的處理.有2中不同的恢復(fù)策略:
A. 進(jìn)行恢復(fù)時(shí)沛简,只重做已經(jīng)提交了的事務(wù)。
B. 進(jìn)行恢復(fù)時(shí)佳遂,重做所有事務(wù)包括未提交的事務(wù)和回滾了的事務(wù)。然后通過(guò)Undo Log回滾那些未提交的事務(wù)丑罪。 - 2.innodb選擇B策略
LOG_BLOCK_HDR_NO是如何計(jì)算的
- 1.在log block header處有一個(gè)稱之為L(zhǎng)OG_BLOCK_HDR_NO的屬性
- 2.這個(gè)屬性代表一個(gè)唯一的標(biāo)號(hào)凤壁。這個(gè)屬性是初次使用該block時(shí)分配的,跟當(dāng)時(shí)的系統(tǒng)lsn值有關(guān)
- 3.具體公司如下:((lsn / 512) & 0x3FFFFFFFUL) + 1煤搜,0x3FFFFFFFUL代表32位比特中前兩位是0后30位是1
- 4.讓一個(gè)數(shù)和0x3FFFFFFFUL做與運(yùn)算的意思就是要將該值的前2個(gè)比特位的值置為0,這樣該值就肯定小于或等于0x3FFFFFFFUL了
- 5.這也就說(shuō)明了唧席,不論lsn多大嘲驾,((lsn / 512) & 0x3FFFFFFFUL)的值肯定在0~0x3FFFFFFFUL之間
- 6.LOG_BLOCK_HDR_NO的第一個(gè)比特位比較特殊辽故,成為flushbit腐碱,如果為1代表本block是在某次將log buffer中的block刷新到磁盤的操作中的第一個(gè)被刷入的block