MySQL InnoDB Update和Crash Recovery流程
轉載自 沃趣科技(ID:woqutech)
作者 羅小波
1、首先介紹了Redo,Undo,Log Sequence Number (LSN),Checkpoint,Rollback Pointer (ROLL_PTR),Transaction ID (TRX_ID),Transaction Serialization Number(TRX_NO) 是什麼?
2、然後介紹了MySQL Update過程中發生了什麼?Redo,Undo,雙寫之間如何配合,髒頁何時重新整理?
3、最後介紹了Crash Recovery時如何做恢復?
1、InnoDB 術語和概念
我們首先來InnoDB的一些基本屬於和概念,以便更好地理解下文中介紹的Update和Crash Recovery流程
1.1. InnoDB概述圖
1.2. InnoDB 重要術語和概念
1.2.1. 什麼是Redo?
通常也會叫做"InnoDB log(s)",預先分配至少2個日誌檔案,第一個檔案開頭和最後一個檔案結尾進行首尾相連以迴圈的方式重複使用。"Redo"的意思是在必要時(如:崩潰恢復時)可以使用Redo Log中的資料來重新應用到InnoDB資料檔案中,使得InnoDB能夠恢復到一個一致性狀態
Redo Log 是一個預寫日誌(WAL),是一種用於在資料庫或資料庫所在主機發生崩潰時確保資料完整性的技術。Redo Log日誌記錄必須在資料實際更改(buffer pool中的髒頁重新整理到資料檔案)之前寫入磁碟。因此,對資料表做修改時,每個資料記錄的修改都會寫入Redo Log Buffer中(作為重做日誌記錄)。當一個頁面的修改操作完成時,Redo Log Buffer將執行flush到Redo Log檔案操作(但此時可能並未sync到磁碟)
根據WAL日誌先行原則,buffer pool中的髒頁被重新整理到資料檔案中之前,需要確保對應LSN號的Redo Log先sync到磁碟檔案中,Redo Log的刷盤機制以及髒頁的刷盤機制確保了Redo Log總是先於髒頁落盤。另外,如果系統引數innodb_flush_log_at_trx_commit設定為1,則每個事務提交時也會將Redo Log sync到磁碟檔案中
Redo Log 日誌組結構
1.2.2. 什麼是Undo?
用於撤消(或還原)對InnoDB中儲存的資料的變更及回滾事務,也用於實現多版本控制(mvcc),透過構建一致性檢視(read view)實現對資料庫的一致性讀
對資料庫的每一次更改,Undo Log都會儲存之前版本的資料,每個聚簇(PK)索引記錄都有一個指向該修改記錄之前版本資料的指標(稱為“回滾指標”),每個Undo Log記錄都會儲存一個回滾指標指向之前版本的資料,另外,每個Undo Log的變更也必須記錄到Redo Log中
PS:Undo Log在共享表空間的基本結構
在共享表空間的第6個頁存放了InnoDB的事務系統資訊,其中也包含了Undo Log的一些系統資訊
InnoDB事務系統資訊頁結構
InnoDB事務系統最多可以建立128個回滾段(MySQL 8.x版本除外),每個回滾段中都需要有一個單獨的page來維護其擁有的undo solt(通常是每個回滾段中的第一個頁),每個回滾段有1024個事務槽,每個事務槽指標都指向每個回滾段中的第一個UNDO_lOG頁中的回滾段頭
Undo Log的資料儲存在系統表空間的UNDO_LOG頁中,下面分別是UNDO_LOG頁結構、UNDO_LOG頁中UNDO頁頭部和UNDO段頭部、UNDO_LOG記錄格式示意圖
1.2.3. 什麼是Log Sequence Number (LSN)?
一個64位無符號整數,表示Redo Log系統中的時間點,也是事務寫入Redo Log的位元組總量,從日誌初始化開始計數(資料庫初始化安裝時間點開始且單調遞增)
LSN不僅存在於Redo Log中,在每個資料頁中都儲存著一個LSN,在進行資料恢復時透過LSN做比較運算可以判斷出每個資料頁是否需要進行恢復操作
1.2.4. 什麼是Checkpoint?
-
一個時間點,由一個LSN值(Checkpoint LSN)表示的整型值,在Checkpoint LSN之前的每個資料頁(buffer pool中的髒頁)的更改都已經落盤(重新整理到資料檔案中),Checkpoint 完成後,在Checkpoint LSN之前的Redo Log就不再需要了
-
Checkpoint技術是為了解決:全量Redo Log恢復時間太長、buffer pool中的空閒頁不夠用時將髒頁重新整理到磁碟資料檔案、Redo Log空間不夠用時將髒頁重新整理到磁碟資料檔案等問題
-
Checkpoint方式有兩種:Sharp Checkpoint和Fuzzy Checkpoint(又可根據不同的場景細分)
-
Sharp Checkpoint:將所有的髒頁刷回磁碟,資料庫例項關閉時系統引數innodb_fast_shutdown設定為0,才需要把所有的髒頁都刷回磁碟,刷髒時系統hang住
-
Fuzzy Checkpoint:持續的每次只重新整理一部分髒頁到磁碟,資料庫正常執行過程中都是使用這種方式刷髒,在InnoDB內部還可細分為如下幾種:
-
Master執行緒每秒/每十秒固定執行Checkpoint
-
LRU list中空閒頁不夠時,觸發Checkpoint從LRU list重新整理髒頁以釋放足夠的空閒頁
-
Redo Log空間不夠時,觸發Checkpoint從Flush list重新整理髒頁,Checkpoint執行完成之後,在這個位置之前的Redo Log不再需要(即,可以迴圈覆蓋使用)
-
髒頁太多達到髒頁比例閥值(系統引數innodb_max_dirty_pages_pct和innodb_max_dirty_pages_pct_lwm控制髒頁比例閥值),觸發Checkpoint
1.2.5. 什麼是Rollback Pointer (ROLL_PTR)?
-
一個由rollback segment number、page number和page offset組成的指標,指向Undo Log中包含之前版本資料的具體Undo Log日誌記錄
-
可用於為任何資料記錄回退到一個歷史版本記錄、可用於mvcc中重建舊版本記錄、可用於事務回滾
1.2.6. 什麼是Transaction ID (TRX_ID)?
-
表示事務開始點的一個64位無符號整數
-
每個事務的事務號增量增加
-
事務號會寫入聚簇索引的每個記錄中
-
最大事務號會寫入系統表空間的TRX_SYS頁
1.2.7. 什麼是Transaction Serialization Number(TRX_NO) ?
-
一個64位無符號整數,表示事務提交時的最大TRX_ID
-
TRX_NO在事務提交時會寫入Undo Log Header
-
TRX_NO可用於purge Undo Log中的舊版本記錄
2、Update流程
2.1. 事務start(事務首次開啟)
-
為這個事務分配事務ID(TRX_ID),該事務ID可能被寫入系統表空間的TRX_SYS頁面中的最大的事務ID欄位(Transaction ID)
-
如果系統表空間的TRX_SYS頁面中的最大的事務ID欄位被更新,則該更新會被記錄到Redo Log中
-
根據分配的TRX_ID建立read view
-
2.2. 記錄修改(每次只修改一行記錄)
-
分配Undo Log日誌空間
-
複製該記錄修改之前的值到Undo Log中
-
將Undo Log的修改記錄寫入Redo Log中
-
在buffer pool中修改資料頁,回滾段指標指向Undo Log中該記錄之前的版本
-
將該記錄對應的資料頁變更部分寫入Undo Log中
-
buffer pool中該記錄修改之後的資料頁被標記為"髒頁"(需要重新整理到磁碟的資料頁)
2.3. 此時其他事務的修改會怎樣?
-
一旦記錄被修改,即使沒有提交,其他事務也可能會看到被修改後的記錄,這依賴於他們的事務隔離級別而定 :
-
如果是RU隔離級別,則使用索引頁讀取最新版本記錄
-
如果是RC隔離級別,則查詢記錄的最新提交版本
-
如果是RR隔離級別,則查詢與其read view相對應的記錄版本
-
任何需要使用索引頁來讀取比最新的版本記錄舊的版本記錄時,都必須使用Undo Log來重建之前的版本記錄
2.4. 事務提交(顯式和隱式提交)
-
事務對應的Undo Log頁被設定為"purge"(意味著當這個Undo Log頁不再被任何其他事務引用時可以將其清除)
-
將Undo Log的修改記錄寫入Redo Log中
-
Redo Log Buffer重新整理到磁碟(是否刷盤取決於系統變數innodb_flush_log_at_trx_commit的設定)
2.5. 後臺執行緒刷髒(後臺執行緒連續不斷地根據不同觸發機制觸發重新整理)
-
查詢最舊的“髒”頁面(修改時間最早的頁面)並將其新增到flush batch中
-
確保在flush batch中中最新的LSN號已經寫入到了Redo Log中且已經落盤
-
如果開啟了雙寫,則先將髒頁重新整理到雙寫緩衝區(並等待同步)
-
將每個髒頁從buffer pool中寫入最終目的地:表空間檔案中的
PS:對於後臺執行緒刷髒部分,執行重新整理髒頁時,與該髒頁的事務是否提交無關,只需要確保該頁對應LSN號的Redo Log記錄落盤,而不會去判斷事務的狀態是否是提交還是未提交狀態,因為,資料頁結構中並沒有地方單獨記錄事務的狀態(即,無法判斷事務是否提交),只是在每行資料中有記錄事務號、回滾段指標(所以一個頁中也可能包含多個事務的修改記錄)。當需要對某個事務進行回滾時,重新從表空間中讀取這個未提交的髒頁,使用undo log中的反向資料進行反向修改,然後再重新刷髒。
2.6. 定期執行Checkpoint
-
確保比Checkpoint 點更舊(比Checkpoint LSN小)的髒頁已重新整理到表空間檔案,如果存在有比Checkpoint LSN大的髒頁,則立即重新整理髒頁到資料檔案中。說白了Checkpoint機制主要作用就是用於重新整理髒頁
-
把Checkpoint LSN寫到Redo Log Header中 (從這個Checkpoint LSN開始,之前的Redo Log記錄不再需要)
2.7. 後臺執行緒Purge(後臺執行緒連續不斷地根據需要定期執行Purge,包括Undo Log和歷史連結串列)
-
查詢每個回滾段中不再需要的最舊的Undo Log
-
實際上是從索引中刪除任何帶有刪除標記的記錄
-
釋放Undo Log頁
-
修剪history lists
3、Creash Recovery流程
3.1. 什麼時候會進行Crash Recovery?
-
例項崩潰之後重啟
-
使用一個備份還原(如:LVM 快照、xtrabackup備份)
-
在“快速”(innodb_fast_shutdown不為0值關閉例項)關閉例項後重新啟動
3.2. 檢測例項是不是乾淨地關閉的
-
開啟Redo Logs和系統表空間檔案(ibdataN)
-
讀取並從中找到最大的Checkpoint LSN
-
從最近的Checkpoint 開始往後掃描Redo Log
-
如果能夠找到Redo Log記錄,說明還有資料頁的更改沒有重新整理到資料檔案上,啟動Crash Recovery,使用Redo Log來恢復資料的一致性
3.3. 使用所有獨立表空間的表名和表空間ID建立一個名稱到ID的對映
-
開啟datadir下的所有.ibd檔案
-
在這些表空間檔案的offset 0的頁(FSP_HDR頁)頭讀取其表空間ID(FSP_HDR頁中FSP Header的前四個位元組記錄著表空間ID)
-
將表空間ID與表名建立對映
PS:
page offset 0(FSP_HDR頁)頁結構
FSP_HDR頁中FSP Header的前四個位元組記錄著表空間ID
3.4. 損壞頁修復(檢查是否有不完整的頁,如果有則使用Double Write Buffer進行修復)
-
檢查雙寫緩衝區中的所有128個頁:
-
讀取表空間中的每個“目標”頁
-
如果頁頭和頁尾的LSN不匹配或頁面校驗和無效,則使用雙寫緩衝區中的頁進行還原
-
如果該頁在雙寫緩衝區中的版本也被破壞,則server將crash
3.5. 前滾Redo,回滾未提交事務
-
事務系統初始化(回滾段初始化)
-
從最近的Checkpoint 往後掃描到的Redo Log記錄將被應用到各個資料檔案中
-
從Undo Log中恢復處於'ACTIVE'狀態的事務,重新生成read view
-
使用Undo Log回滾未提交的'ACTIVE'狀態的事務
-
處於PREPARE狀態的事務,如果開啟了binlog且在binlog有找到對應事務的日誌則重新提交,否則回滾
4、參考資料
本文大部分為譯文,原文PDF下載連結:
-
%20-%20A%20journey%20to%20the%20core%20-%20PLMCE%202013.pdf
-
%20-%20A%20journey%20to%20the%20core%20II.pdf
-
%20-%20A%20journey%20to%20the%20core%20III.pdf
-
解析表空間工具:https://blog.jcole.us/2013/01/03/a-quick-introduction-to-innodb-ruby/
-
深入瞭解InnoDB學習資料:https://blog.jcole.us/innodb/
-
MySQL · 引擎特性 · InnoDB 檔案系統之檔案物理結構:
-
關於2.5. 小節的PS部分,參考了oracle的資料,個人覺得MySQL也是同樣的邏輯:http://blog.csdn.net/dba_waterbin/article/details/7823519
-
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2145161/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL InnoDB update流程MySql
- MySQL:Innodb crash recovery一些程式碼MySql
- InnoDB update操作流程圖流程圖
- MySQL update資料時InnoDB內部的操作流程MySql
- Crash recovery和Media recovery的區別
- MySQL crash recovery恢復慢分析MySql
- postgres crash recovery
- 【案例】利用innodb_force_recovery解決MySQL伺服器crash無法重啟問題MySql伺服器
- 【案例】利用innodb_force_recovery 解決MySQL伺服器crash無法重啟問題MySql伺服器
- fast recovery for innodb1.07 in Mysql 5.5ASTMySql
- 【Oracle】-Difference between Instance recovery and Crash RecoveryOracle
- Overview of Instance and Crash RecoveryView
- mysql innodb之select for update nowaitMySqlAI
- mysql innodb新建索引堵塞update ,insert,deleteMySql索引delete
- SQL Server進行Crash RecoverySQLServer
- MySQL -update語句流程總結MySql
- InnoDB事務鎖之行鎖-delete流程update階段加鎖delete
- innodb_force_recovery設定
- 【MySQL】崩潰恢復問題解決:Forcing InnoDB RecoveryMySql
- MySQL修復壞塊引數innodb_force_recovery的解釋MySql
- mysql事務和鎖InnoDBMySql
- MySQL/InnoDB和Group CommitMySqlMIT
- MySQL/InnoDB和GroupCommit(2)MySqlMIT
- 早上MYSQL突然 CRASHMySql
- MySQL·原始碼分析·InnoDB非同步IO工作流程MySql原始碼非同步
- 深入解讀MySQL InnoDB儲存引擎Update語句執行過程MySql儲存引擎
- 『淺入淺出』MySQL 和 InnoDBMySql
- MYSQL和INNODB分層實現MySql
- MySQL 優化六(InnoDB 下 update 資料出現表鎖之優化)MySql優化
- mysql crash with disk sector errorMySqlError
- Latch導致MySQL CrashMySql
- Mysql innodb引擎(一)緩衝和索引MySql索引
- Mysql 中 MyISAM 和 InnoDB 的區別MySql
- mysql innodb_log_file_size 和innodb_log_buffer_size引數MySql
- MySQL InnoDB 索引MySql索引
- mysql bin-log和log-update開啟MySql
- 使用Percona Data Recovery Tool for InnoDB恢復資料
- mysql update join優化update in查詢效率MySql優化