1. 程式人生 > >MySQL 5.7並行複製中並行的真正含義

MySQL 5.7並行複製中並行的真正含義

 

MySQL 5.7並行複製中並行的真正含義

 

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/joy0921/article/details/80130768

MySQL 5.7並行複製初理解

我們知道MySQL 5.7並行複製引入了兩個值last_committedsequence_numberlast_committed表示事務提交的時候,上次事務提交的編號,在主庫上同時提交的事務設定成相同的last_committed。如果事務具有相同的last_committed,表示這些事務都在一組內,可以進行並行的回放。這個機制也是Commit-Parent-Based SchemeWL#6314

中的實現方式。不過之後,官方對這種模式做了改進,所以最新的並行回放機制和WL#6314有了不同,詳情見Lock-Based SchemeWL#7165

下面介紹一下舊模式Commit-Parent-Based SchemeWL#6314和新模式Lock-Based SchemeWL#7165的不同之處,以及改進的地方。

Commit-Parent-Based Scheme WL#6314

Commit-Parent-Based Scheme簡介

  • 在master上,有一個全域性計數器(global counter)。在每一次儲存引擎提交之前,計數器值就會增加。
  • 在master上,在事務進入prepare階段之前,全域性計數器的當前值會被儲存在事務中。這個值稱為此事務的commit-parent
  • 在master上,commit-parent會在事務的開頭被儲存在binlog中。
  • 在slave上,如果兩個事務有同一個commit-parent,他們就可以並行被執行。

commit-parent就是我們在binlog中看到的last_committed。如果commit-parent相同,即last_committed相同,則被視為同一組,可以並行回放。

Commit-Parent-Based Scheme的問題

一句話:Commit-Parent-Based Scheme會降低複製的並行程度。

圖片描述

解釋一下圖:

  • 水平虛線表示事務按時間順序往後走。

  • P表示事務在進入prepare階段之前讀到的commit-parent值的那個時間點。可以簡單的視為加鎖時間點。

  • C表示事務增加了全域性計數器(global counter)的值的那個時間點。可以簡單的視為釋放鎖的時間點

  • P對應的commit-parentlast_commited)是取自所有已經執行完的事務的最大的C對應的sequence_number。舉例來說:

    • Trx4的P對應的commit-parentlast_commited)取自所有已經執行完的事務的最大的C對應的sequence_number,也就是Trx1的C對應的sequence_number。因為這個時候Trx1已經執行完,但是Trx2還未執行完。
    • Trx5的P對應的commit-parentlast_commited)取自所有已經執行完的事務的最大的C對應的sequence_number,也就是Trx2的C對應的sequence_number;Trx6的P對應的commit-parentlast_commited)取自所有已經執行完的事務的最大的C對應的sequence_number,也就是Trx2的C對應的sequence_number。所以Trx5和Trx6具有相同的commit-parentlast_commited),在進行回放的時候,Trx5和Trx6可以並行回放。

由圖可見,Trx5 和 Trx6可以併發執行,因為他們的commit-parent是相同的,都是由Trx2設定的。但是,Trx4和Trx5不能併發執行, Trx6和Trx7也不能併發執行。

我們可以注意到,在同一時段,Trx4和Trx5、Trx6和Trx7分別持有他們各自的鎖,事務互不衝突。所以,如果在slave上併發執行,也是不會有問題的。

根據以上例子,可以得知:

  • Trx4、Trx5和Trx6在同一時間持有各自的鎖,但Trx4無法併發執行。
  • Trx6和Trx7在同一時間持有各自的鎖,但Trx7無法併發執行。

但是,實際上,Trx4是可以和Trx5、Trx6並行執行,Trx6可以和Trx7並行執行。

如果能實現這個,那麼並行複製的效果會更好。所以官方對並行複製的機制做了改進,提出了一種新的並行複製的方式:Lock-Based Scheme

Lock-Based Scheme WL#7165

實現:如果兩個事務在同一時間持有各自的鎖,就可以併發執行。

Lock-Based Scheme簡介

首先,定義了一個稱為lock interval的概念,含義:一個事務持有鎖的時間間隔。

  • 當儲存引擎提交,第一把鎖釋放,lock interval結束。
  • 當最後一把鎖獲取,lock interval開始。假定:最後一把鎖獲取是在binlog_prepare階段。

假設有兩個事務:Trx1、Trx2。Trx1先於Trx2。那麼,當且僅當Trx1、Trx2的lock interval有重疊,則可以並行執行。換言之,當且僅當Trx1結束自己的lock interval要早於Trx2開始自己的lock interval,則不能並行執行。

圖片描述

L表示lock interval的開始點 
C表示lock interval的結束

對於C(lock interval的結束點),MySQL會給每個事務分配一個邏輯時間戳(logical timestamp),命名為:transaction.sequence_number。此外,MySQL會獲取全域性變數global.max_committed_transaction,含義:所有已經結束lock interval的事務的最大的sequence_number

對於L(lock interval的開始點),MySQL會把global.max_committed_timestamp分配給一個變數,並取名叫transaction.last_committed

transaction.sequence_numbertransaction.last_committed這兩個時間戳都會存放在binlog中。

根據以上分析,我們可以得出在slave上執行事務的條件:

如果所有正在執行的事務的最小的sequence_number大於一個事務的transaction.last_committed,那麼這個事務就可以併發執行。

換言之:slave的work執行緒不能開始執行一個事務,直到這個事務的last_committed值小於所有其他正在執行事務的sequence_number

根據以上分析,回過頭來看前面的那幅圖:

圖片描述

可以看到Trx3、Trx4、Trx5、Trx6四個事務可以併發執行。因為Trx3的sequence_number大於Trx4、Trx5、Trx6的last_committed,所以可以併發執行。

當Trx3、Trx4、Trx5執行完成之後,Trx6和Trx7可以併發執行。因為Trx6的sequence_number大於Trx7的last_committed,即兩者的lock interval存在重疊。Trx5和Trx7不能併發執行,因為:Trx5的sequence_number小於Trx7的last_committed,即兩者的lock interval不存在重疊。

綜上所述,可以有三種方法來判斷slave上事務是否可以並行執行:

  • 假設有兩個事務:Trx1、Trx2。Trx1先於Trx2。那麼,當且僅當Trx1、Trx2的lock interval有重疊,則可以並行執行。
  • 如果所有正在執行的事務的最小的sequence_number大於一個事務的transaction.last_committed,那麼這個事務就可以併發執行。
  • slave 的work執行緒不能開始執行一個事務,直到這個事務的last_committed值小於所有其他正在執行事務的sequence_number

由上分析,新模式Lock-Based Scheme機制的併發度比舊模式Commit-Parent-Based Scheme的併發度要好。 
下面舉一個例子,詳細描述基於Lock-Based Scheme並行複製的整個過程。

Lock-Based Scheme例子

圖片描述

對於第一個事務,last_committed=0,sequence_number=1。第一個work執行緒會接手這個事務並開始工作。

第二個事務,last_committed=1sequence_number=2。直到第一個事務完成,這個事務才能開始。因為last_committed=1不小於正在執行執行事務的sequence_number=1。所以這兩個事務只能序列。

第三個事務,last_committed=2sequence_number=3。和之前情況一樣,只有等到sequence_number=2的事務完成才能開始。

第四個事務,last_committed=3 sequence_number=4。同樣如此。

雖然前四個事務可能會被分配到不同的work執行緒,但實際上他們是序列的,就像單執行緒複製那樣。

sequence_number=4的事務完成,last_committed=4的三個事務就可以併發執行。

last_committed=4 sequence_number=5 
last_committed=4 sequence_number=6 
last_committed=4 sequence_number=7

一旦前兩個執行完成,下面這兩個可以開始執行:

last_committed=6 sequence_number=8 
last_committed=6 sequence_number=9

sequence_number=7正在執行的時候,sequence_number=8sequence_number=9這兩個也可以併發執行。

這三個事務的結束沒有前後順序的限制。因為這三個事務的lock interval有重疊,因此可以併發執行,所以事務之間並不會相互影響。

等到前面的事務均完成之後,下面這個事務才可以進行:

last_committed=9 sequence_number=10

Lock-Based Scheme往前的併發度計算

從上面的描述,可以發現,如果sequence_number - last_committed的差值越大,併發度會越高。如果差值為1,那麼這個事務必須等到前面的事務完成才能開始執行。如果差值為2,那麼這個事務可以和前面的一個事務併發執行。

和後面的事務是否能併發進行,這個當前事務是無法判斷的;當前事務只能與前面事務的sequence_number比較,得出自己是否可以併發執行。

我們可以通過以下命令粗略檢視併發度:

mysqlbinlog mysql-bin.000124 | grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $4-$2}' | sort -g | uniq -c
854 1
1846 2
2573 3
3145 4
3628 5
4076 6
4591 7
5192 8
5741 9
6371 10
7024 11
7646 12
8299 13
8976 14
9592 15
10274 16
10871 17
11392 18
11832 19
12245 20
12316 21
12405 22
11958 23
12951 24
160 25
 1 50
 1 66
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

差值為1的有854個,只是表示這854個事務的sequence_number - last_committed=1,必須等到所有前面的事務完成之後才能開始;但,並不表示不能和後面的事務併發執行。比如,上面的那個例子:

last_committed=4 sequence_number=5 
last_committed=4 sequence_number=6 
last_committed=4 sequence_number=7

sequence_number=5的這個事務只是表明必須等到前面事務完成,此事務才能開始執行;但它可以和後面的另兩個事務併發執行。

我們也可以通過以下的方式來優化並行複製的work數:

https://www.percona.com/blog/2016/02/10/estimating-potential-for-mysql-5-7-parallel-replication/

引申:slave_preserve_commit_order引數

這個引數設定為yes是為了確保,在slave上事務的提交順序與relay log中一致。

但是經過測試,這個引數在MySQL 5.7.18中設定之後,也無法保證slave上事務提交的順序與relay log一致。

在MySQL 5.7.19設定後,slave上事務的提交順序與relay log中一致。

For multi-threaded slaves, enabling this variable ensures that transactions are externalized on the slave in the same order as they appear in the slave’s relay log. Setting this variable has no effect on slaves for which multi-threading is not enabled. All replication threads (for all replication channels if you are using multiple replication channels) must be stopped before changing this variable. –log-bin and –log-slave-updates must be enabled on the slave. In addition –slave-parallel-type must be set to LOGICAL_CLOCK.

參考

作者:韓傑 
來源:沃趣科技(woqutech)