天天看點

詳細分析MySQL事務日志(undo log)2.undo log3.binlog和事務日志的先後順序及group commit

2.undo log

2.1 基本概念

undo log有兩個作用:提供復原和多個行版本控制(MVCC)。

在資料修改的時候,不僅記錄了redo,還記錄了相對應的undo,如果因為某些原因導緻事務失敗或復原了,可以借助該undo進行復原。

undo log和redo log記錄實體日志不一樣,它是邏輯日志。可以認為當delete一條記錄時,undo log中會記錄一條對應的insert記錄,反之亦然,當update一條記錄時,它記錄一條對應相反的update記錄。

當執行rollback時,就可以從undo log中的邏輯記錄讀取到相應的内容并進行復原。有時候應用到行版本控制的時候,也是通過undo log來實作的:當讀取的某一行被其他事務鎖定時,它可以從undo log中分析出該行記錄以前的資料是什麼,進而提供該行版本資訊,讓使用者實作非鎖定一緻性讀取。

undo log是采用段(segment)的方式來記錄的,每個undo操作在記錄的時候占用一個undo log segment。

另外,undo log也會産生redo log,因為undo log也要實作持久性保護。

2.2 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預設存放在共享表空間中。

[[email protected] data]# ll /mydata/data/ib*
-rw-rw---- 1 mysql mysql 79691776 Mar 31 01:42 /mydata/data/ibdata1
-rw-rw---- 1 mysql mysql 50331648 Mar 31 01:42 /mydata/data/ib_logfile0
-rw-rw---- 1 mysql mysql 50331648 Mar 31 01:42 /mydata/data/ib_logfile1      

如果開啟了 innodb_file_per_table ,将放在每個表的.ibd檔案中。

在MySQL5.6中,undo的存放位置還可以通過變量 innodb_undo_directory 來自定義存放目錄,預設值為"."表示datadir。

預設rollback segment全部寫在一個檔案中,但可以通過設定變量 innodb_undo_tablespaces 平均配置設定到多少個檔案中。該變量預設值為0,即全部寫入一個表空間檔案。該變量為靜态變量,隻能在資料庫示例停止狀态下修改,如寫入配置檔案或啟動時帶上對應參數。但是innodb存儲引擎在啟動過程中提示,不建議修改為非0的值,如下:

2017-03-31 13:16:00 7f665bfab720 InnoDB: Expected to open 3 undo tablespaces but was able
2017-03-31 13:16:00 7f665bfab720 InnoDB: to find only 0 undo tablespaces.
2017-03-31 13:16:00 7f665bfab720 InnoDB: Set the innodb_undo_tablespaces parameter to the
2017-03-31 13:16:00 7f665bfab720 InnoDB: correct value and retry. Suggested value is 0      

2.3 和undo log相關的變量

undo相關的變量在MySQL5.6中已經變得很少。如下:它們的意義在上文中已經解釋了。

mysql> show variables like "%undo%";
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| innodb_undo_directory   | .     |
| innodb_undo_logs        | 128   |
| innodb_undo_tablespaces | 0     |
+-------------------------+-------+      

2.4 delete/update操作的内部機制

當事務送出的時候,innodb不會立即删除undo log,因為後續還可能會用到undo log,如隔離級别為repeatable read時,事務讀取的都是開啟事務時的最新送出行版本,隻要該事務不結束,該行版本就不能删除,即undo log不能删除。

但是在事務送出的時候,會将該事務對應的undo log放入到删除清單中,未來通過purge來删除。并且送出事務時,還會判斷undo log配置設定的頁是否可以重用,如果可以重用,則會配置設定給後面來的事務,避免為每個獨立的事務配置設定獨立的undo log頁而浪費存儲空間和性能。

通過undo log記錄delete和update操作的結果發現:(insert操作無需分析,就是插入行而已)

  • delete操作實際上不會直接删除,而是将delete對象打上delete flag,标記為删除,最終的删除操作是purge線程完成的。
  • update分為兩種情況:update的列是否是主鍵列。
    • 如果不是主鍵列,在undo log中直接反向記錄是如何update的。即update是直接進行的。
    • 如果是主鍵列,update分兩部執行:先删除該行,再插入一行目标行。

3.binlog和事務日志的先後順序及group commit

如果事務不是隻讀事務,即涉及到了資料的修改,預設情況下會在commit的時候調用fsync()将日志刷到磁盤,保證事務的持久性。

但是一次刷一個事務的日志性能較低,特别是事務集中在某一時刻時事務量非常大的時候。innodb提供了group commit功能,可以将多個事務的事務日志通過一次fsync()刷到磁盤中。

因為事務在送出的時候不僅會記錄事務日志,還會記錄二進制日志,但是它們誰先記錄呢?二進制日志是MySQL的上層日志,先于存儲引擎的事務日志被寫入。

在MySQL5.6以前,當事務送出(即發出commit指令)後,MySQL接收到該信号進入commit prepare階段;進入prepare階段後,立即寫記憶體中的二進制日志,寫完記憶體中的二進制日志後就相當于确定了commit操作;然後開始寫記憶體中的事務日志;最後将二進制日志和事務日志刷盤,它們如何刷盤,分别由變量 sync_binlog 和 innodb_flush_log_at_trx_commit 控制。

但因為要保證二進制日志和事務日志的一緻性,在送出後的prepare階段會啟用一個prepare_commit_mutex鎖來保證它們的順序性和一緻性。但這樣會導緻開啟二進制日志後group commmit失效,特别是在主從複制結構中,幾乎都會開啟二進制日志。

在MySQL5.6中進行了改進。送出事務時,在存儲引擎層的上一層結構中會将事務按序放入一個隊列,隊列中的第一個事務稱為leader,其他事務稱為follower,leader控制着follower的行為。雖然順序還是一樣先刷二進制,再刷事務日志,但是機制完全改變了:删除了原來的prepare_commit_mutex行為,也能保證即使開啟了二進制日志,group commit也是有效的。

MySQL5.6中分為3個步驟:flush階段、sync階段、commit階段。

詳細分析MySQL事務日志(undo log)2.undo log3.binlog和事務日志的先後順序及group commit
  • flush階段:向記憶體中寫入每個事務的二進制日志。
  • sync階段:将記憶體中的二進制日志刷盤。若隊列中有多個事務,那麼僅一次fsync操作就完成了二進制日志的刷盤操作。這在MySQL5.6中稱為BLGC(binary log group commit)。
  • commit階段:leader根據順序調用存儲引擎層事務的送出,由于innodb本就支援group commit,是以解決了因為鎖 prepare_commit_mutex 而導緻的group commit失效問題。

在flush階段寫入二進制日志到記憶體中,但是不是寫完就進入sync階段的,而是要等待一定的時間,多積累幾個事務的binlog一起進入sync階段,等待時間由變量 binlog_max_flush_queue_time 決定,預設值為0表示不等待直接進入sync,設定該變量為一個大于0的值的好處是group中的事務多了,性能會好一些,但是這樣會導緻事務的響應時間變慢,是以建議不要修改該變量的值,除非事務量非常多并且不斷的在寫入和更新。

進入到sync階段,會将binlog從記憶體中刷入到磁盤,刷入的數量和單獨的二進制日志刷盤一樣,由變量 sync_binlog 控制。

當有一組事務在進行commit階段時,其他新事務可以進行flush階段,它們本就不會互相阻塞,是以group commit會不斷生效。當然,group commit的性能和隊列中的事務數量有關,如果每次隊列中隻有1個事務,那麼group commit和單獨的commit沒什麼差別,當隊列中事務越來越多時,即送出事務越多越快時,group commit的效果越明顯。

轉載于:https://www.cnblogs.com/drizzle-xu/p/9713513.html