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

MySQL5.7並行複製中並行的真正含義

MySQL5.7並行複製中並行的真正含義
MySQL5.7並行複製初理解
Commit-Parent-Based Scheme WL#6314
Commit-Parent-Based Scheme簡介
Commit-Parent-Based Scheme的問題
Lock-Based Scheme WL#7165
Lock-Based Scheme簡介
Lock-Based Scheme例子
Lock-Based Scheme 往前的併發度計算
引申:slave_preserve_commit_order引數

參考

MySQL5.7並行複製中並行的真正含義

MySQL5.7並行複製初理解

  • 我們知道MySQL5.7並行複製引入了兩個值last_committed和sequence_number。last_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-parent(last_commited)是取自所有已經執行完的事務的最大的C對應的sequence_number。舉例來說: 
      - Trx4的P對應的commit-parent(last_commited)取自所有已經執行完的事務的最大的C對應的sequence_number,也就是Trx1的C對應的sequence_number。因為這個時候Trx1已經執行完,但是Trx2還未執行完。 
      - Trx5的P對應的commit-parent(last_commited)取自所有已經執行完的事務的最大的C對應的sequence_number,也就是Trx2的C對應的sequence_number;Trx6的P對應的commit-parent(last_commited)取自所有已經執行完的事務的最大的C對應的sequence_number,也就是Trx2的C對應的sequence_number。所以Trx5和Trx6具有相同的commit-parent(last_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_number和transaction.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=1, sequence_number=2。直到第一個事務完成,這個事務才能開始。因為last_committed=1不小於正在執行執行事務的sequence_number=1。所以這兩個事務只能序列。
  • 第三個事務,last_committed=2, sequence_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=8和sequence_number=9這兩個也可以併發執行。 
    這三個事務的結束沒有前後順序的限制。因為這三個事務的lock interval有重疊,因此可以併發執行,所以事務之間並不會相互影響。 
    注:實際上這三個事務是否存在提交的前後順序的限制,取決於是否設定了slave_preserve_commit_order引數。如果此引數設定為on,則這三個事務在slave上提交的順序和主庫上一致;如果此引數設定為off,則這三個事務在slave上提交的順序不能保證與主庫上一致,也就可能會造成slave的複製存在gap的情況。何為gap:A gap is a transaction that has not been (fully) applied, even though some later transaction has been applied.

  • 等到前面的事務均完成之後,下面這個事務才可以進行: 
    last_committed=9 sequence_number=10

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

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

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

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

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

    差值為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中一致。 
    但是經過測試,這個引數在MySQL5.7.18中設定之後,也無法保證slave上事務提交的順序與relay log一致。 
    在MySQL5.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.

參考

https://dev.mysql.com/worklog/task/?id=7165 
https://dev.mysql.com/worklog/task/?id=6314 
http://thenoyes.com/littlenoise/?p=525 
https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#sysvar_slave_preserve_commit_order