導讀
作為MySQL DBA都應該知道,Redo Log是可被覆蓋的,是ACID中的D的最重要的構成部分,也就是關係型數據庫中的WAL中的L。
Redo Log記錄的是redo,那麼redo是什麼呢?通俗來講,redo記錄的是對應的記錄改變的物理操作。說實話,過去的很長一段時間內,我對redo的認識也僅限於此,並沒有好好深入理解redo記錄的到底是什麼。這次從redo的物理結構上深入理解下redo到底是什麼。
Redo Log邏輯&物理結構
從邏輯上來講,redo log記錄是連續遞增的,但是對應到物理文件就不一樣了,考慮到磁盤空間,redo log被設計成了多個可循環寫入的文件。InnoDB要求Redo Log,文件至少有2個,初始文件為 ib_logfile0和 ib_logfile1, ib_logfile0寫完以後寫 ib_logfile1,等到 ib_logfile1也寫完了,從頭又開始寫 ib_logfile0,這樣就形成了一個環形寫入的結構。但是覆蓋寫入的前提是要確定哪個位置點是可以覆蓋寫的,哪些位置是不能覆蓋寫的,這個就是check point的工作了,關於checkpoint可以關注我上一篇文章《MySQL Checkpoint》。
Log File物理結構
從 ib_logfile0和 ib_logfile1這兩個文件的物理結構可以看出,在Log Header部分還是有些許差異的, ib_logfile0會多一些額外的信息,主要是checkpoint信息。
並且每個Block的單位是512字節,對應到磁盤每個扇區也是512字節,因此redo log寫磁盤是原子寫,保證能夠寫成功,而不像index page一樣需要double write來保證安全寫入。
我們依次從上到下來看每個Block的結構
Log File Header Block
- Log Goup ID,可能會配置多個redo組,每個組對應一個id,當前都是0,佔用4字節
- Start LSN,這個redo log文件開始日誌的lsn,佔用8字節
- Log File Number,總是為0,佔用4字節
- Created By,備份程序所佔用的字節數,佔用32字節
另外在ib_logfile0中會有兩個checkpoint block,分別是 LOG_CHECKPOINT_1/ LOG_CHECKPOINT_2,兩個記錄InnoDB Checkpoint信息的字段,分別從文件頭的第二個和第四個block開始記錄,並且只在每組log的第一個文件中存在,組內其他文件雖然沒有checkpoint相關信息,但是也會預留相應的空間出來。這裡為什麼有兩個checkpoint的呢?原因是設計為交替寫入,避免因為介質失敗而導致無法找到可用的checkpoint的情況。
Log blocks
log block結構分為日誌頭段、日誌記錄、日誌尾部
- Block Header,佔用12字節
- Data部分
- Block tailer,佔用4字節
Block Header
這個部分是每個Block的頭部,主要記錄的塊的信息
- Block Number,表示這是第幾個block,佔用4字節,是通過LSN計算得來的,佔用4字節
- Block data len,表示該block中有多少字節已經被使用了,佔用2字節
- First Rec offet,表示該block中作為第一個新的mtr開始的偏移量,佔用2字節
- Checkpoint number,表示該log block最後被寫入時的檢查點的值,佔用4字節
Data部分
這部分才開始真正記錄我們理解的redo log,實際真正可用字節數為512-12-4=496字節,用戶的redo是以一條一條的記錄存放在這個block的data部分,並且一條redo記錄可能會佔用多個block
Block tailfer
tailer部分就比較簡單了,只是記錄一個checksum值,用於正確性校驗,佔用4字節
Log Record
沒錯,redo記錄就是這個結構,分為頭部、body部分
通用header
- redo_log_type,重做日誌的類型
- space ID,表空間ID
- Page Numer,用於定位哪個page
redo包括幾種類型,M_LOG_WRITE_1BYTE、M_LOG_WRITE_2BYTE、M_LOG_WRITE_4BYTE和M_LOG_WRITE_STRING等,其頭部格式如下所示
一條完整的INSERT redo record
關於LSN
LSN幾乎是redo中最重要的概念之一了,LSN表示redo的寫入量,標識了checkpoint的位置,標識了page的版本。LSN不僅存在與redo log中,還存在於每個page中。在每個page的頭部,有一個 FIL_PAGE_LSN,記錄了page的LSN,表示該頁最後刷新時的LSN大小。redo中記錄的是每個page的日誌,因此page中的LSN用來判斷是否需要進行恢復操作,這對於MySQL的崩潰恢復及其重要。
關於redo刷盤機制
大概有以下幾種情況會觸發redo log刷盤
- log buffer空間用完了,這就會將已經產生的log buffer中的日誌刷到磁盤中,這是最普遍的一種方式;
- master線程在後臺每秒鐘刷一次,將當前log buffer中的日誌刷到磁盤中;
- 每次執行DML操作時,都會主動檢查日誌空間是否足夠,如果使用空間的量已經超過了一個預設的經驗值,就會主動刷日誌,以保證在後面真正執行時,不會再執行過程中被動的刷盤,但這裡只會是寫文件(寫入OS緩衝中)不會刷盤
- 在做checkpoint的時候,要保證所有要刷的頁面中LSN值最小的日誌已經刷入到磁盤,不然,如果此時數據庫宕機,日誌不存在,但數據頁面已經被修改,從而導致數據不一致,就違背了寫日誌的原則;
- 提交邏輯事務時,會因為參數 innodb_flush_log_at_trx_commit值的不同,產生不同的行為。如果設置0,則在事務提交時,不會去刷日誌緩衝區,等待master thread以固定頻率去刷盤,這種設置是最危險的 如果設置2,則在事務提交時會將日誌寫入到文件中,但不會去刷盤,只要操作系統不掛,即使數據庫掛了,數據還是不會丟失 如果設置1,則在事務提交的時候將日誌寫入文件同時fsync,保證redo log落盤,生產環境主庫強烈建議設置為1
幾個建議:
- innodb_flush_log_at_trx_commit設置為1
- redo log建議設置2G,組數建議為3組以上,避免因為redo log切換導致性能抖動
- redo log buffer建議設置32M以上(根據實際情況至少能夠緩存1秒的redo)