淺析MySQL group commit及group replication

前言:mysql

本文做者:沃趣科技MySQL數據庫工程師  麻鵬飛sql

 

 

1、binlog提交的流程數據庫

 

1、MySQL沒有開啓Binary log的狀況下: 緩存

l  InnoDB存儲引擎經過redo和undo日誌能夠safe crash recovery數據庫,當數據crash recovery時,經過redo日誌將全部已經在存儲引擎內部提交的事務應用redo log恢復,全部已經prepared可是沒有commit的transactions將會應用undo log作roll back。而後客戶端鏈接時就能看到已經提交的數據存在數據庫內,未提交被回滾地數據須要從新執行。安全

 

2、MySQL開啓Binary log 的狀況下:微信

l  爲了保證存儲引擎和MySQL數據庫上層的二進制日誌保持一致(由於備庫經過二進制日誌重放主庫提交的事務,假設主庫存儲引擎已經提交而二進制日誌沒有保持一致,則會使備庫數據丟失形成主備數據不一致),引入二階段提交(two phase commit or 2pc) session

 

3、二階段提交流程多線程

圖一 二階段提交併發

1)Storage Engine(InnoDB) transaction prepare階段:即sql語句已經成功執行並生成redo和undo的內存日誌分佈式

2)Binary log日誌提提交

l  write()將binary log內存日誌數據寫入文件系統緩存

l  fsync()將binary log 文件系統緩存日誌數據永久寫入磁盤

3)Storage Engine(InnoDB)內部提交

commit階段在存儲引擎內提交( innodb_flush_log_at_trx_commit控制)使undo和redo永久寫入磁盤

4、開啓Binary logMySQLcrash recovery時:

l  當事務在prepare階段crash,數據庫recovery的時候該事務未寫入Binary log而且存儲引擎未提交,將該事務roll back。

l  當事務在Binary log日誌已經fsync()永久寫入二進制日誌時crash,可是存儲引擎將來得及commit,此時MySQL數據庫recovery的時候將會從二進制日誌的Xid(MySQL數據庫內部分佈式事務XA)中獲取提交的信息從新將該事務重作並commit使存儲引擎和二進制日誌始終保持一致。

 

5、以上提到單個事務的二階段提交過程,可以保證存儲引擎和binary log日誌保持一致,可是在併發的狀況下怎麼保證存儲引擎和Binary Log提交的順序一致?當多個事務併發提交的狀況,若是Binary Log和存儲引擎順序不一致會形成什麼影響?

2 InnoDB存儲引擎提交的順序與MySQL上層的二進制日誌順序不一樣

如上圖:事務按照T一、T二、T3順序開始執行,將二進制日誌(按照T一、T二、T3順序)寫入日誌文件系統緩存,調用fsync()進行一次group commit將日誌文件永久寫入磁盤,可是存儲引擎提交的順序爲T二、T三、T1。當T二、T3提交事務以後作了一個On-line的backup程序新建一個slave來作replication,那麼事務T1在slave機器restore MySQL數據庫的時候發現未在存儲引擎內提交,T1事務被roll back,此時主備數據不一致(搭建Slave時,change master to的日誌偏移量記錄T3在事務位置以後)。

 

結論:MySQL數據庫上層二進制日誌的寫入順序和存儲引擎InnoDB層的事務提交順序一致,用於備份及恢復須要,如xtrabackup和innobackpex工具。爲了解決以上問題,在早期的MySQL版本,經過prepare_commit_mutex 鎖保證MySQ數據庫上層二進制日誌和Innodb存儲引擎層的事務提交順序一致。

3 經過prepare_commit_mutex保證存儲引擎和二進制日誌順序提交順序一致

 

圖3能夠看出在prepare_commit_mutex,只有當上一個事務commit後釋放鎖,下一個事務才能夠進行prepara操做,而且在每一個transaction過程當中Binary log沒有fsync()的調用。因爲內存數據寫入磁盤的開銷很大,若是頻繁fsync()把日誌數據永久寫入磁盤數據庫的性能將會急劇降低。此時MySQL 數據庫提供sync_binlog參數來設置多少個binlog日誌產生的時候調用一次fsync()把二進制日誌刷入磁盤來提升總體性能,該參數的設置做用:

l  sync_binlog=0,二進制日誌fsync()的操做基於操做系統。

l  sync_binlog=1,每個transaction commit都會調用一次fsync(),此時能保證數據最安全可是性能影響較大。

l  sync_binlog=N,當數據庫crash的時候至少會丟失N-1個transactions。

 

圖3 所示MySQL開啓Binary log時使用prepare_commit_mutex和sync_log保證二進制日誌和存儲引擎順序保持一致(經過sync_binlog來控制日誌的刷新頻率),prepare_commit_mutex的鎖機制形成高併發提交事務的時候性能很是差並且二進制日誌也沒法group commit。

 

6、那麼如何保證MySQL開啓Binary Log日誌後使二進制日誌寫入順序和存儲引擎提交順序保持一致而且可以進行二進制日誌的Group Commit

MySQL 5.6 引入BLGC(Binary Log Group Commit),二進制日誌的提交過程分紅三個階段,Flush stage、Sync stage、Commit stage。

 

那麼事務提交過程簡化爲:

存儲引擎(InnoDB) Prepare    ---->    數據庫上層(Binary Log)   Flush Stage    ---->    Sync Stage    ---->    調存儲引擎(InnoDB)Commit stage.

 

每一個stage階段都有各自的隊列,使每一個session的事務進行排隊。當一個線程註冊了一個空隊列,該線程就視爲該隊列的leader,後註冊到該隊列的線程爲follower,leader控制隊列中follower的行爲。leader同時帶領當前隊列的全部follower到下一個stage去執行,當遇到下一個stage並不是空隊列,此時leader能夠變成follower到此隊列中(注:follower的線程不可能變成leader)

 

4: 二進制日誌三階段提交過程

在 Flush stage:全部已經註冊線程都將寫入binary log緩存

 

在Sync stage :binary log緩存的數據將會sync到磁盤,當sync_binlog=1時全部該隊列事務的二進制日誌緩存永久寫入磁盤

 

在 Commit stage:leader根據順序調用存儲引擎提交事務。

 

當一組事務在進行Commit階段時,其餘新的事務能夠進行Flush階段,從而使group commit不斷生效。那麼爲了提升group commit中一組隊列的事務數量,MySQL用binlog_max_flush_queue_time來控制在Flush stage中的等待時間,讓Flush隊列在此階段多等待一些時間來增長這一組事務隊列的數量使該隊列到Sync階段能夠一次fsync()更多的事務。

 

MySQL 5.7 Parallel replication實現主備多線程複製基於主庫Binary Log Group Commit, 並在Binary log日誌中標識同一組事務的last_commited=N和該組事務內全部的事務提交順序。爲了增長一組事務內的事務數量提升備庫組提交時的併發量引入了binlog_group_commit_sync_delay=N 和binlog_group_commit_sync_no_delay_count=N (注:binlog_max_flush_queue_time 在MySQL的5.7.9及以後版本再也不生效)參數,MySQL等待binlog_group_commit_sync_delay毫秒直到達到binlog_group_commit_sync_no_delay_count事務個數時,將進行一次組提交。

 

 

2、併發複製的多線程複製

 

首先梳理下傳統MySQL/MariaDB主備複製基本原理:

主從複製經過三個線程來完成,在master節點運行的binlog dump的線程,I/O線程和SQL線程運行在slave 節點

master節點的Binlog dump線程,當slave節點與master正常鏈接的時候,master把更新的binlog 內容推送到slave節點。

slave節點的I/O 線程 ,該線程經過讀取master節點binlog日誌名稱以及偏移量信息將其拷貝到本地relay log日誌文件。

slave節點的SQL線程,該線程讀取relay log日誌信息,將在master節點上提交的事務在本地回放,達到與主庫數據保持一致的目的。

那麼問題來了。

問題1

Master節點的數據庫實例併發跑多個線程同時提交事務,提交的事務按照邏輯的時間(數據庫LSN號)順序地寫入binary log日誌,,slave節點經過I/O線程寫到本地的relay log日誌,可是slave節點只有SQL單線程來執行relay log中的日誌信息重放主庫提交得事務,形成主備數據庫存在延遲(lag)

思考1:

那麼爲了減小主備數據同步延遲時間,因爲備庫只有單線程補償數據的緣由而形成延遲,那麼可否使slave節點同時運行多個如SQL線程同樣的功能來重放在主庫執行的事務?答案固然是:能夠!可是咱們須要解決如下問題:

一、slave本地的relay log記錄的是master 的binary log日誌信息,日誌記錄的信息按照事務的時間前後順序記錄,那麼爲了保證主備數據一致性,slave節點必須按照一樣的順序執行,若是順序不一致容易形成主備庫數據不一致的風險。

例1

Master上執行順序:t1,t2

Slave上執行順序:t2,t1

 

MySQL 5.6改進:

MySQL 5.6版本引入併發複製(schema級別),基於schema級別的併發複製核心思想:「不一樣schema下的表併發提交時的數據不會相互影響,即slave節點能夠用對relay log中不一樣的schema各分配一個相似SQL功能的線程,來重放relay log中主庫已經提交的事務,保持數據與主庫一致」。可見MySQL5.6版本的併發複製,一個schema分配一個相似SQL線程的功能。

 

實現1      

slave節點開啓併發複製(slave_parallel_workers=3),當前的slave的SQL線程爲Coordinator(協調器),執行relay log日誌的線程爲worker(當前的SQL線程不只起到協調器的做用,同時也能夠重放relay log中主庫提交的事務)

 

問題2:

MySQL 5.6基於schema級別的併發複製可以解決當業務數據的表放在不一樣的database庫下,可是實際生產中每每大多數或者所有的業務數據表都放在同一個schema下,在這種場景即便slave_parallel_workers>0設置也沒法併發執行relay log中記錄的主庫提交數據。 高併發的狀況下,因爲slave沒法併發執行同個schema下的業務數據表,依然會形成主備延遲的狀況。

 

思考2

那麼若是slave同時能夠用多線程的方式,同時執行一個schema下的全部業務數據表,將能大大提升slave節點執行ralay log中記錄的主庫提交事務達到與主庫數據同步的目的,實現該功能咱們須要解決什麼問題?

 

一、前面提到過爲了保證主庫數據一致性,master節點寫入的binary log日誌按照數據庫邏輯時間前後的順序而且slave節點執行relay log中主庫提交的事務必須按照一致的順序不然會形成主備數據不一致的狀況。

二、既然要實現scehma下全部的業務數據表可以併發執行,那麼slave必須得知道併發執行relay log中主庫提交的事務不能相互影響並且結果必須和主庫保持一致。

 

實現2:

MySQL 5.7 引入Enhanced Muti-threaded slaves,當slave配置slave_parallel_workers>0而且global.slave_parallel_type=‘LOGICAL_CLOCK’,可支持一個schema下,slave_parallel_workers個的worker線程併發執行relay log中主庫提交的事務。可是要實現以上功能,須要在master機器標記binary log中的提交的事務哪些是能夠併發執行,雖然MySQL 5.6已經引入了binary log group commit,可是沒有將能夠併發執行的事務標記出來。

 

咱們用命令 mysqlbinlog -vvv mysqlbinlog.0000003 | grep -i last_committed    在MySQL 5.7的master機器上能夠看到last_committed 和sequence_number

1.  #151223 15:11:28 server id 15102  end_log_pos 14623 CRC32 0x767a33fa GTID      last_committed=18         sequence_number=26

 

slave機器的relay log中 last_committed相同的事務(sequence_num不一樣)能夠併發執行。從上面截取的信息能夠看出last_committed=26的事務一共有8個:從sequence_number=27~24。假設當slave_parallel_workers=7時,Coordinator線程(SQL線程)分配這一組事務到worker中排隊去執行。這裏能夠看出增長master庫binary log group commit組中事務的數量能夠提升slave機器併發處理事務的數量,MySQL5.7引入 binlog_group_commit_sync_delay和 binlog_group_commit_sync_no_delay_count參數即提升binary log組提交併發數量。MySQL等待binlog_group_commit_sync_delay毫秒的時間直到binlog_group_commit_sync_no_delay_count個事務數時,將進行一次組提交。

 

總結:

MySQL 5.7 GA版本推出的 Enhanced Multi-threaded Slaves功能,完全解決了以前版本主備數據複製延遲的問題,開啓該功能參數以下:

1.  # slave機器

2.  slave-parallel-type=LOGICAL_CLOCK

3.  #slave-parallel-type=DATABASE #兼容MySQL 5.6基於schema級別的併發複製

4.  slave-parallel-workers=16 #開啓多線程複製

5.  master_info_repository=TABLE

6.  relay_log_info_repository=TABLE

7.  relay_log_recovery=ON

 

 

關於commit問題的幾個思考:

1、主從同步環境裏,事務是否commit和主從真的有關係?

 

2、若是是半同步環境裏,事務是否commit和主從有什麼關係?

 

爲了方便你們交流,本人開通了微信公衆號,和QQ羣291519319。喜歡技術的一塊兒來交流吧

相關文章
相關標籤/搜索