您好,登錄后才能下訂單哦!
MySQL數(shù)據(jù)庫意外掉線后數(shù)據(jù)該怎么恢復(fù),很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
InnoDB如果發(fā)生意外宕機(jī)了,數(shù)據(jù)會丟么?
對于這個(gè)問題,稍微了解一點(diǎn)MySQL知識的人,都會斬釘截鐵的回答:不會!
為什么?
他們也會毫不猶豫地說:因?yàn)橛兄刈鋈罩荆╮edo log),數(shù)據(jù)可以通過redo log進(jìn)行恢復(fù)。
回答得很好,那么InnoDB怎樣通過redo log進(jìn)行數(shù)據(jù)恢復(fù)的,具體的流程是怎樣的?
估計(jì)能說清楚這個(gè)問題的人所剩不多了,更深入一點(diǎn):除了redo log,InnoDB在恢復(fù)過程中,還需要其他信息么?比如是否需要binlog參與?undo日志在恢復(fù)過程中又會起到什么作用?
到這里,可能很多人會變得疑惑起來:數(shù)據(jù)恢復(fù)跟undo有半毛錢的關(guān)系?
其實(shí),InnoDB的數(shù)據(jù)恢復(fù)是一個(gè)很復(fù)雜的過程,這個(gè)恢復(fù)過程需要redo log、binlog、undo log等參與。這里把InnoDB的恢復(fù)過程主要劃分為兩個(gè)階段:
第一階段主要依賴于redo log的恢復(fù);
而第二階段,恰恰需要binlog和undo log的共同參與。
接下來,我們來具體了解下整個(gè)恢復(fù)的過程:
一、依賴redo log進(jìn)行恢復(fù)
第一階段,數(shù)據(jù)庫啟動后,InnoDB會通過redo log找到最近一次checkpoint的位置,然后根據(jù)checkpoint相對應(yīng)的LSN開始,獲取需要重做的日志,接著解析獲取的日志并且保存到一個(gè)哈希表中,最后通過遍歷哈希表中的redo log信息,讀取相關(guān)頁進(jìn)行恢復(fù)。
InnoDB的checkpoint信息保存在日志文件中,即ib_logfile0的開始2048個(gè)字節(jié)中,checkpoint有兩個(gè),交替更新,checkpoint與日志文件的關(guān)系如下圖:
(checkpoint位置)
checkpoint信息分別保存在ib_logfile0的512字節(jié)和1536字節(jié)處,每個(gè)checkpoint默認(rèn)大小為512字節(jié),InnoDB的checkpoint主要由3部分信息組成:
checkpoint no:主要保存的是checkpoint號,因?yàn)镮nnoDB有兩個(gè)checkpoint,通過checkpoint號來判斷哪個(gè)checkpoint更新。
checkpoint lsn:主要記錄了產(chǎn)生該checkpoint是flush的LSN,確保在該LSN前面的數(shù)據(jù)頁都已經(jīng)落盤,不再需要通過redo log進(jìn)行恢復(fù)。
checkpoint offset:主要記錄了該checkpoint產(chǎn)生時(shí),redo log在ib_logfile中的偏移量,通過該offset位置就可以找到需要恢復(fù)的redo log開始位置。
通過以上checkpoint的信息,我們可以簡單得到需要恢復(fù)的redo log的位置,然后通過順序掃描該redo log來讀取數(shù)據(jù),比如我們通過checkpoint定位到開始恢復(fù)的redo log位置在ib_logfile1中的某個(gè)位置,那么整個(gè)redo log掃描的過程可能是這樣的:
(redo log掃描過程)
Step 1:從ib_logfile1的指定位置開始讀取redo log,每次讀取4 * page_size的大小,這里我們默認(rèn)頁面大小為16K,所以每次讀取64K的redo log到緩存中,redo log每條記錄(block)的大小為512字節(jié)。
Step 2:讀取到緩存中的redo log通過解析、驗(yàn)證等一系列過程后,把redo log的內(nèi)容部分保存到用于恢復(fù)的緩存recv_sys->buf,保存到恢復(fù)緩存中的每條信息主要包含兩部分:(space,offset)組成的位置信息和具體redo log的內(nèi)容,我們稱之為body。
Step 3:同時(shí)保存在恢復(fù)緩存中的redo信息會根據(jù)(space,offset)計(jì)算一個(gè)哈希值后保存到一個(gè)哈希表(recv_sys->addr_hash)中,相同哈希值、不同(space,offset)用鏈表存儲,相同的(space,offset)用列表保存,可能部分事務(wù)比較大,redo信息一個(gè)block不能保存,所以,每個(gè)body中可以用鏈表鏈接多body的值。
redo log被保存到哈希表中之后,InnoDB就可以開始進(jìn)行數(shù)據(jù)恢復(fù),只需要輪詢哈希表中的每個(gè)節(jié)點(diǎn)獲取redo信息,根據(jù)(space,offset)讀取指定頁面后進(jìn)行日志覆蓋。
在上面整個(gè)過程中,InnoDB為了保證恢復(fù)的速度,做了幾點(diǎn)優(yōu)化:
優(yōu)化1:
在根據(jù)(space,offset)讀取數(shù)據(jù)頁信息到buffer pool的時(shí)候,InnoDB不是只讀取一張頁面,而是讀取相鄰的32張頁面到buffer pool。這里有個(gè)假設(shè),InnoDB認(rèn)為,如果一張頁面被修改了,那么其周圍的一些頁面很有可能也被修改了,所以一次性連續(xù)讀入32張頁面可以避免后續(xù)再重新讀取。
優(yōu)化2:
在MySQL5.7版本以前,InnoDB恢復(fù)時(shí)需要依賴數(shù)據(jù)字典,因?yàn)镮nnoDB根本不知道某個(gè)具體的space對應(yīng)的ibd文件是哪個(gè),這些信息都是數(shù)據(jù)字典維護(hù)的。而且在恢復(fù)前,需要把所有的表空間全部打開,如果庫中有數(shù)以萬計(jì)的表,把所有表打開一遍,整個(gè)過程就會很慢。那么MySQL5.7在這上面做了哪些改進(jìn)呢?
其實(shí)很簡單,針對上面的問題,InnoDB在redo log中增加了兩種redo log的類型來解決。
MLOG_FILE_NAME
用于記錄在checkpoint之后,所有被修改過的信息(space,filepath);
MLOG_CHECKPOINT
則用于標(biāo)志MLOG_FILE_NAME的結(jié)束。
上面兩種redo log類型的添加,完美解決了前面遺留的問題,redo log中保存了后續(xù)需要恢復(fù)的space和filepath對。所以,在恢復(fù)的時(shí)候,只需要從checkpoint的位置一直往后掃描到MLOG_CHECKPOINT的位置,這樣就能獲取到需要恢復(fù)的space和filepath。在恢復(fù)過程中,只需要打開這些ibd文件即可。當(dāng)然由于space和filepath的對應(yīng)關(guān)系通過redo存了下來,恢復(fù)的時(shí)候也不再依賴數(shù)據(jù)字典。
這里需要強(qiáng)調(diào)的是MLOG_CHECKPOINT在每個(gè)checkpoint點(diǎn)中最多存在一次,如果出現(xiàn)多次MLOG_CHECKPOINT類型的日志,則說明redo已經(jīng)損壞,InnoDB會報(bào)錯。
最多存在一次,那么會不會有不存在的情況?
答案是肯定的,在每次checkpoint過后,如果沒有發(fā)生數(shù)據(jù)更新,那么MLOG_CHECKPOINT就不會被記錄。所以只要查找下redo log最新一個(gè)checkpoint后的MLOG_CHECKPOINT是否存在,就能判定上次MySQL是否正常關(guān)機(jī)。
5.7版本的MySQL在InnoDB進(jìn)行恢復(fù)的時(shí)候,也正是這樣做的,MySQL5.7在進(jìn)行恢復(fù)的時(shí)候,一般情況下需要進(jìn)行最多3次的redo log掃描:
1、首先對redo log的掃描,主要是為了查找MLOG_CHECKPOINT,這里并不進(jìn)行redo log的解析。如果你沒有找到MLOG_CHECKPOINT,則說明InnoDB不需要進(jìn)行recovery,后面的兩次掃描可以省略;如果找到了MLOG_CHECKPOINT,則獲取MLOG_FILE_NAME到指定列表,后續(xù)只需打開該鏈表中的表空間即可。
2、下一步的掃描是在第一次找到MLOG_CHECKPOINT基礎(chǔ)之上進(jìn)行的,該次掃描會把redo log解析到哈希表中,如果掃描完整個(gè)文件,哈希表還沒有被填滿,則不需要第三次掃描,直接進(jìn)行recovery就結(jié)束。
3、最后是在第二次基礎(chǔ)上進(jìn)行的,第二次掃描把哈希表填滿后,還有redo log剩余,則需要循環(huán)進(jìn)行掃描,哈希表滿后立即進(jìn)行recovery,直到所有的redo log被apply完為止。
redo log全部被解析并且apply完成,整個(gè)InnoDB recovery的第一階段也就結(jié)束了,在該階段中,所有已經(jīng)被記錄到redo log但是沒有完成數(shù)據(jù)刷盤的記錄都被重新落盤。
然而,InnoDB單靠redo log的恢復(fù)是不夠的,這樣還是有可能會丟失數(shù)據(jù)(或者說造成主從數(shù)據(jù)不一致)。
因?yàn)樵谑聞?wù)提交過程中,寫binlog和寫redo log提交是兩個(gè)過程,寫binlog在前而redo提交在后,如果MySQL寫完binlog后,在redo提交之前發(fā)生了宕機(jī),這樣就會出現(xiàn)問題:binlog中已經(jīng)包含了該條記錄,而redo沒有持久化。binlog已經(jīng)落盤就意味著slave上可以apply該條數(shù)據(jù),redo沒有持久化則代表了master上該條數(shù)據(jù)并沒有落盤,也不能通過redo進(jìn)行恢復(fù)。
這樣就造成了主從數(shù)據(jù)的不一致,換句話說主上丟失了部分?jǐn)?shù)據(jù),那么MySQL又是如何保證在這樣的情況下,數(shù)據(jù)還是一致的?這就需要進(jìn)行第二階段恢復(fù)。
二、binlog和undo log共同參與
前面提到,在第二階段恢復(fù)中,需要用到binlog和undo log,下面我們就來看下具體的恢復(fù)邏輯是怎樣的?
其實(shí)該階段的恢復(fù)中,也被劃分成兩部分:第一部分,根據(jù)binlog獲取所有可能沒有提交事務(wù)的xid列表;第二部分,根據(jù)undo中的信息構(gòu)造所有未提交事務(wù)鏈表,最后通過上面兩部分協(xié)調(diào)判斷事務(wù)是否可以提交。
(根據(jù)binlog獲取xid列表)
如上圖所示,MySQL在第二階段恢復(fù)的時(shí)候,先會去讀取最后一個(gè)binlog文件的所有event信息,然后把xid保存到一個(gè)列表中,然后進(jìn)行第二部分的恢復(fù),如下:
(基于undo構(gòu)造事務(wù)鏈表)
我們知道,InnoDB當(dāng)前版本有128個(gè)回滾段,每個(gè)回滾段中保存了undo log的位置指針,通過掃描undo日志,我們可以構(gòu)造出還未被提交的事務(wù)鏈表(存在于insert_undo_list和update_undo_lsit中的事務(wù)都是未被提交的),所以通過起始頁(0,5)下的solt信息可以定位到回滾段,然后根據(jù)回滾段下的undo的slot定位到undo頁,把所有的undo信息構(gòu)建一個(gè)undo_list,然后通過undo_list再創(chuàng)建未提交事務(wù)鏈表trx_sys->trx_list。
基于上面兩步, 我們已經(jīng)構(gòu)建了xid列表和未提交事務(wù)列表,那么在這些未提交事務(wù)列表中的事務(wù),哪些需要被提交?哪些又該回滾?
判斷條件很簡單:凡是xid在通過binlog構(gòu)建的xid列表中存在的事務(wù),都需要被提交。換句話說,所有已經(jīng)記錄binlog的事務(wù),需要被提交,而剩下那些沒有記錄binlog的事務(wù),則需要被回滾
三、回顧優(yōu)化
通過上述兩個(gè)階段的數(shù)據(jù)恢復(fù),InnoDB才最終完成整個(gè)recovery過程,回過頭來我們再想想,在上述兩個(gè)階段中,是否還有優(yōu)化空間?比如第一階段,在構(gòu)造完哈希表后,事務(wù)的恢復(fù)是否可以并發(fā)進(jìn)行?理論上每個(gè)hash node是根據(jù)(space,offset)生成的,不同的hash node之間不存在沖突,可以并行進(jìn)行恢復(fù)。
或者在根據(jù)哈希表進(jìn)行數(shù)據(jù)頁讀取時(shí),每次讀取連續(xù)32張頁面,這里讀取的32張頁面,可能有部分是不需要的,也同時(shí)被讀入到Buffer Pool中了,是否可以在構(gòu)建一顆紅黑樹,根據(jù)(space,offset)組合鍵進(jìn)行插入,這樣如果需要恢復(fù)的時(shí)候,可以根據(jù)紅黑樹的排序原理,把所有頁面的讀取順序化,并不需要讀取額外的頁面。
看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注億速云行業(yè)資訊頻道,感謝您對億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。