深刻理解MySQL系列之redo log、undo log和binlog

事務的實現

redo log保證事務的持久性,undo log用來幫助事務回滾及MVCC的功能。html

InnoDB存儲引擎體系結構

redo log

Write Ahead Log策略

事務提交時,先寫重作日誌再修改頁;當因爲發生宕機而致使數據丟失時,就能夠經過重作日誌來完成數據的恢復。數據庫

  • InnoDB首先將重作日誌信息先放到重作日誌緩存
  • 按必定頻率刷新到重作日誌文件

重作日誌文件: 在默認狀況,InnoDB存儲引擎的數據目錄下會有兩個名爲ib_logfile1和ib_logfile2的文件。每一個InnoDB存儲引擎至少有1個重作日誌文件組(group),每一個文件組下至少有2個重作日誌文件。緩存

下面圖一,很好說明重作日誌組以循環寫入方式運行,InnoDB存儲引擎先寫ib_logfile1,當達到文件最後時,會切換至重作日誌文件ib_logfile2.數據結構

而圖2,增長一個OS Buffer,有助於理解fsync過程。併發

在這裏插入圖片描述

關於log group,稱爲重作日誌組,是一個邏輯上的概念。InnoDB存儲引擎實際只有一個log group。
mvc

log group中第一個redo log file,其前2KB部分保存4個512字節大小塊:異步

重作日誌緩衝刷新到磁盤

下面三種狀況刷新:函數

  • Master Thread每一秒將重作日誌緩衝刷新到重作日誌文件
  • 每一個事務提交時會將重作日誌緩衝刷新到重作日誌文件
  • 當重作日誌緩衝池剩餘空間小於1/2時,重作日誌刷新到重作日誌文件

補充上述三種狀況第二種,觸發寫磁盤過程由參數innodb_flush_log_at_trx_commit控制,表示提交(commit)操做時,處理重作日誌的方式。性能

參數innodb_flush_log_at_trx_commit有效值有0、一、2spa

  • 0表示當提交事務時,並不將事務的重作日誌寫入磁盤上日誌文件,而是等待主線程每秒刷新。
  • 1表示在執行commit時將重作日誌緩衝同步寫到磁盤,即伴有fsync的調用
  • 2表示將重作日誌異步寫到磁盤,即寫到文件系統的緩存中。不保證commit時確定會寫入重作日誌文件。

0,當數據庫發生宕機時,部分日誌未刷新到磁盤,所以會丟失最後一段時間的事務。
2,當操做系統宕機時,重啓數據庫後會丟失未從文件系統緩存刷新到重作日誌文件那部分事務。

下圖有助於理解

在這裏插入圖片描述

重作日誌塊

在InnoDB存儲引擎中,重作日誌都是以512字節進行存儲的。意味着重作日誌緩存、重作日誌文件都是以塊(block)的方式進行保存的,每塊512字節。

重作日誌頭12字節,重作日誌尾8字節,故每一個重作日誌塊實際能夠存儲492字節。

重作日誌格式

redo log是基於頁的格式來記錄的。默認狀況下,innodb的頁大小是16KB(由 innodb_page_size變量控制),一個頁內能夠存放很是多的log block(每一個512字節),而log block中記錄的又是數據頁的變化。

log body的格式分爲4部分:

  • redo_log_type:佔用1個字節,表示redo log的日誌類型。
  • space:表示表空間的ID,採用壓縮的方式後,佔用的空間可能小於4字節。
  • page_no:表示頁的偏移量,一樣是壓縮過的。
  • redo_log_body表示每一個重作日誌的數據部分,恢復時會調用相應的函數進行解析。例如insert語句和delete語句寫入redo log的內容是不同的。

在這裏插入圖片描述

以下圖,分別是insert和delete大體的記錄方式。

在這裏插入圖片描述

redo日誌恢復

下面LSN(Log Sequence Number)表明checkpoint,當數據庫在LSN爲10000時發生宕機,恢復操做僅恢復LSN10000-LSN13000範圍內日誌

undo log

undo log的做用

undo是邏輯日誌,只是將數據庫邏輯地恢復到原來的樣子;全部修改都被邏輯地取消了,可是數據結構和頁自己在回滾以後可能不大相同。

undo log有兩個做用:提供回滾和多個行版本控制(MVCC)。

  • InnoDB存儲引擎回滾時,對於每一個INSERT,會完成一個DELETE;對於每一個DELETE,會執行一個INSERT;對於每一個UPDATE,會執行一個相反的UPDATE,將修改前的行放回去。

  • MVCC: 當用戶讀取一行記錄時,若該記錄已經被其餘事務佔用,當前事務能夠經過undo讀取以前的行版本信息,以此實現非鎖定讀取。

undo log的存儲方式

innodb存儲引擎對undo的管理採用段的方式。rollback segment稱爲回滾段,每一個回滾段中有1024個undo log segment。

在之前老版本,只支持1個rollback segment,這樣就只能記錄1024個undo log segment。後來MySQL5.5能夠支持128個rollback segment,即支持128*1024個undo操做,還能夠經過變量 innodb_undo_logs (5.6版本之前該變量是 innodb_rollback_segments )自定義多少個rollback segment,默認值爲128。

undo log默認存放在共享表空間中。

在這裏插入圖片描述

事務提交undo log處理過程

當事務提交時,InnoDB存儲引擎會作如下兩件事:

  • 將undo log放入一個列表中,以供以後的purge使用,是否能夠最終刪除undo log及所在頁由purge線程來判斷
  • 判斷undo log 所在的頁是否能夠重用,若能夠,分配給下個事務使用

當事務提交時,首先將undo log放入鏈表中,而後判斷undo頁的使用空間是否小於3/4,如果,則表示該undo頁能夠被重用,以後新的undo log記錄在當前undo log的後面

undo log分爲:

  • insert undo log
  • update undo log

由於事務隔離性,insert undo log對其餘事務不可見,因此該undo log能夠在事務提交後直接刪除,不須要進行purge操做。

update undo log記錄的是對delete和update操做產生的undo log。該undo log可能須要提供MVCC機制,所以不能提交時就進行刪除

update分爲兩種狀況:

  1. update的列若是不是主鍵列,在undo log中直接反向記錄是如何update的。即update是直接進行的。
  2. update主鍵的操做能夠分爲兩步:
  • 首先將原主鍵記錄標記爲已刪除,所以須要產生一個類型爲TRX_UNDO_DEL_MARK_REC的undo log
  • 以後插入一條新的記錄,產生一個類型爲TRX_UNDO_INSERT_MARK_REC的undo log

InnoDB purge時,會先從history列表找undo log,而後再從undo page中找undo log;能夠避免大量隨機讀取操做,從而提升purge效率。

MVCC(多版本併發控制)

MVCC其實就是在每一行記錄後面增長兩個隱藏列,記錄建立版本號和刪除版本號,而每個事務在啓動的時候,都有一個惟一的遞增的版本號。

MVCC只在REPEATABLE READ 和READ COMMITTED兩個隔離級別下工做。讀未提交不存在版本問題,序列化則對全部讀取行加鎖。

示例:

  1. 插入操做:記錄的建立版本號就是事務版本號

如插入一條記錄,事務id假設是1,則建立版本號也是1

id name create version delete version
1 test 1
  1. 更新操做:先標記舊版本號爲已刪除,版本號就是當前版本號,再插入一條新的記錄

如事務2把name字段更新
update table set name = 'new test' where id = 1;

原來的記錄被標記刪除,刪除版本號爲2,並插入新記錄,建立版本號爲2

id name create version delete version
1 test 1 2
1 new test 2
  1. 刪除操做:把事務版本做爲刪除版本號

如事務3把記錄刪除
delete from table where id = 1;

id name create version delete version
1 test 2 3
  1. 查詢操做

需知足如下兩個條件的記錄才能被事務查詢出來:

  • InnoDB只查找版本早於當前事務版本的數據行
  • 行的刪除版本要麼未定義,要麼大於當前版本號,這能夠確保事務讀取到的行,在事務未開始以前未被刪除

MVCC好處:減小鎖的爭用,提高性能

binlog

二進制文件概念及做用

二進制文件(binary log)記錄了對MySQL數據庫執行更改的全部操做(不包含SELECT、SHOW等,由於對數據沒有修改)

二進制文件主要幾種做用:

  • 恢復:某些數據的恢復須要二進制日誌
  • 複製: 經過複製和執行二進制日誌使一臺遠程的MySQL(slave)與另外一臺MySQL數據庫(master)進行實時同步
  • 審計: 用戶能夠經過二進制日誌中信息來進行審計,判斷是否有對數據庫進行注入的攻擊
二進制文件三個格式

MySQL 5.1開始引入binlog_format參數,該參數可設值有STATEMENT、ROW和MIX(三種模式優缺點可參考https://mp.weixin.qq.com/s/KB73550tKpNccW-WKxT7-A)

  1. STATEMENT: 二進制文件記錄的是日誌的邏輯SQL語句
  2. ROW:記錄表的行更改狀況。若是設置了ROW模式,能夠將InnoDB事務隔離級別設爲READ_COMMITTED,以得到更好的併發性
  3. MIX:MySQL默認採用STATEMENT格式進行二進制文件的記錄,但在一些狀況下會使用ROW,可能的狀況有:
  • 表的存儲引擎爲NDB,這時對錶DML操做都以ROW格式進行
  • 使用了UUID()、USER()、CURRENT_USER()、FOUND_ROWS()、ROW_COUNT()等不肯定函數
  • 使用了INSERT DELAY語句
  • 使用了用戶定義函數
  • 使用了臨時表
redo log和二進制文件區別

(二進制文件用來進行POINT-IN-TIME(PIT))的恢復及主從複製環境的創建。

  1. 二進制文件會記錄全部與MySQL數據庫有關的日誌記錄,包括InnoDB、MyISAM等其餘存儲引擎的日誌。而InnoDB存儲引擎的重作日誌只記錄有關該存儲引擎自己的事務日誌。
  2. 記錄的內容不一樣,不管用戶將二進制日誌文件記錄的格式設爲STATEMENT、ROW或MIXED,其記錄的都是關於一個事務的具體操做內容,即該日誌是邏輯日誌。而InnoDB存儲引擎的重作日誌文件記錄的是關於每一個頁的更改的物理狀況。
  3. 此外,寫入的時間頁不一樣,二進制日誌文件僅再事務提交前進行提交,即只寫磁盤一次,不論這時該事務多大。而在事務進行的過程當中,卻不斷有重作日誌條目(reod entry)被寫入到重作日誌文件中。

group commit

若事務爲非只讀事務,則每次事務提交時須要進行一次fsync操做,以此保證重作日誌都已經寫入磁盤。但磁盤fsync性能有限,爲提升磁盤fsync效率,當前數據庫都提供group commit功能,即一次能夠刷新確保多個事務日誌被寫入文件。

對InnoDB group commit,進行兩階段操做:

  • 修改內存中事務對應的信息,而且將日誌寫入重作日誌緩衝
  • 調用fsync將確保日誌都從重作日誌緩衝寫入磁盤

InnoDB1.2前,開啓二進制文件,group commit功能失效問題:

開啓二進制文件後,其步驟以下:
1)當事務提交時,InnoDB存儲引擎進行prepare操做
2)MySQL數據庫上層寫入二進制文件
3)InnoDB將日誌寫入重作日誌文件

  • a)修改內存中事務對應的信息,並將日誌寫入重作日誌緩衝
  • b)調用fsync將確保日誌都從重作日誌緩衝寫入磁盤

其中在保證MySQL數據庫上層二進制文件的寫入順序,和InnoDB事務提交順序一致,MySQL內部使用了prepare_commit_mutex鎖,從而步驟3)中a)步不能夠在其餘事務執行步驟b)時進行,從而致使roup commit功能失效。

在這裏插入圖片描述

解決方案即是BLGC(Binary Log Group Commit)

MySQL 5.6 BLGC實現方式分爲三個階段:

  • Flush階段:將每一個事務的二進制文件寫入內存
  • Sync階段:將內存中的二進制刷新到磁盤,若隊列有多個事務,那麼僅一次fsync操做就完成了二進制日誌的寫入,這就是BLGC
  • Commit階段:leader根據順序調用存儲引擎層事務提交,因爲innodb本就支持group commit,因此解決了由於鎖 prepare_commit_mutex 而致使的group commit失效問題。

在這裏插入圖片描述

參考:
《MySQL技術內幕》
https://mp.weixin.qq.com/s/rNFy_qwnNWUvzjYznOXKJw
https://www.cnblogs.com/hapjin/archive/2019/09/28/11521506.html
https://mp.weixin.qq.com/s/E-iJRPt5YRdLUja8RwzZ3A

相關文章
相關標籤/搜索