若備庫執(zhí)行日志的速度低于主庫生成日志的速度、備庫有可能很久都追不上主庫
MySQL5.6版本之前、MySQL只支持單線程復制、主庫并發(fā)高、TPS較高時、可能會出現(xiàn)主備延遲
5.6版本之后、sql_thread不會直接更新數(shù)據、而是交給 coordinator 來中轉日志和分發(fā)事務、由worker進程來更新數(shù)據. worker的線程數(shù)由 slave_parallel_works 決定.
一般32核機器設置為 8~16、備庫還要提供查詢、不能把CPU都吃光了.
思考:
事務能不能輪詢分發(fā)給各個worker ?
不可以. 事務分發(fā)給worker以后、不同worker就獨立執(zhí)行了、但由于CPU的調度策略、第二個事務可能會比第一個先執(zhí)行、此時、若兩個事務更新的是同一行數(shù)據、就會導致主備不一致.同一個事務的多個更新語句、能不能分發(fā)給多個worker執(zhí)行 ?
不可以. 若Trx1 更新了t1和t2 兩個表的各一行數(shù)據、若分發(fā)到不同的worker執(zhí)行、雖然備庫最終結果是一致的、但 t1更新完成的瞬間、會出現(xiàn)不一致, 破壞了事務的隔離性
所以: 分發(fā)器 coordinator 需要滿足:
- 不能造成覆蓋更新、更新同一行數(shù)據的兩個事務必須分發(fā)到同一個worker
- 同一個事務必須分發(fā)到同一個worker.

不同版本的并行復制策略:
- 5.5版本
官方5.5版本不支持并行復制.按表分發(fā). 每個worker線程維護一個hash表、用來保存當前正在這個worker上執(zhí)行的事務所涉及的表. 有事務分配給該worker時、涉及的表會加入到對應hash表、worker執(zhí)行完成、從hash表移除. 若與多個worker沖突、coordinator進入等待.
事務分發(fā)時、與worker沖突情況:
a. 跟所有worker都不沖突、coordinator直接分發(fā)給最空閑的worker線程
b. 跟多于一個worker沖突、coordinator進入等待、直到和該事務沖突的worker進程只有1個
c. 若只跟一個worker沖突、分配給存在沖突關系的worker.
按表分發(fā)、在多表負載均勻的場景下效果不錯、但若碰到熱點表、比如: 所有的更新事務都涉及同一張表時、所有事務都會分發(fā)到同一個worker、就變成單線程了.按行分發(fā). 若兩個事務沒有更新相同行、可以在備庫并行執(zhí)行、要求binlog格式必須是row.
此時判斷沖突規(guī)則就是 庫名 + 表名 + 唯一鍵值(還要考慮唯一索引)
相比于按表分發(fā)、按行分發(fā)在決定線程分發(fā)的時候、需要消耗更多的計算資源.
按表分發(fā)和按行分發(fā)都有約束:
binlog格式必須是Row
表必須要有主鍵
不能有外鍵、有外鍵時、級聯(lián)更新的行不會記錄binlog、這樣沖突檢測就不準確.
按行分發(fā)有個問題: 大事務耗費CPU和內存.
解決: 會設置一個閾值、若單事務超過設置行閾值(eg. 單事務更新行數(shù)超過10w)、就退化為單線程模式: 1. coordinator暫時hold住事務、2. 等所有worker變成空隊列、coordinator直接執(zhí)行事務 3. 恢復并行模式5.6按庫并行
原理同上、并行效果取決于壓力模型. 若主庫上有多個DB、并且多個DB的壓力均衡、使用庫級別分發(fā)效果較好. 相對表級別和行級別優(yōu)勢:
- 構造hash值很快、只需要庫名、且一個實例上DB數(shù)量不會過多
- 不要求binlog格式為row、statement也可以拿到庫名.
但: 若主庫上的所有表都放在同一個DB里、這個策略就沒用了、若不同DB熱點不同、也起不到并行效果.
- MariaDB的并行復制策略
利用的是組提交的特性.
- 能夠在同一組里提交的事務、一定不會修改同一行
- 主庫上可以并行執(zhí)行的事務、備庫上也一定可以并行執(zhí)行
在一組提交的事務、有一個相同的commit_id、下一組就是 commit_id + 1
commit_id直接寫入binlog
傳到備庫應用時、相同commit_id的事務分發(fā)到多個worker執(zhí)行
一組全部執(zhí)行完、coordinator再取下一組數(shù)據.
可以模擬主庫的并行模式. 但: 并未完全實現(xiàn)模擬主庫并發(fā)度這個目標, 在主庫上、一組事務commit時、下一組事務是同時處于執(zhí)行中的. 而備庫需要第一組事務執(zhí)行完、第二組才能開始執(zhí)行、這樣系統(tǒng)的吞吐量就不夠、另外、容易被大事務拖后腿.
假設 trx1 / trx2 / trx3為一組事務、trx2為大事務、這樣、trx1/trx3執(zhí)行完后也必須等待trx2執(zhí)行完成、下一組才能開始執(zhí)行、這段時間只有一個worker線程在工作、是對資源的浪費.MySQL5.7的并行復制策略
MySQL5.7提供了 slave-parallel-type 控制并行復制策略
slave-parallel-type=database; 表示使用mysql5.6的按庫并行復制
slave-parallel-type=logical_clock; 表示使用類似mariadb的策略、但做了改進
思考: 同時處于執(zhí)行狀態(tài)的所有事務、是不是可以并行 ?
不能. 可能有由于鎖沖突而處于鎖等待狀態(tài)的事務、若這些事務在備庫上分配到不同的worker、會出現(xiàn)主備不一致. 所以、MariaDB的核心是、處于commit狀態(tài)的事務并行處理.
而實際上: 處于redo log prepare階段的事務、都已經通過了鎖沖突檢驗、
所以, Mysql5.7的并行復制策略思想是:
- 同時處于prepare狀態(tài)的事務、在備庫可以并行執(zhí)行
- 處于prepare狀態(tài)的事務、與處于commit狀態(tài)的事務之間、在備庫執(zhí)行時、也是可以并行的.
binlog_group_commit_sync_delay表示延遲多少微秒后調用fsync
binlog_group_commit_sync_no_Delay_count表示累積多少次后才調用fsync
這兩個參數(shù)可以用于故意拉長binlog從write到fsync的時間、減少binlog的寫盤次數(shù)、在5.7的復制策略里、可以用來制造更多處于prepare階段的事務、增加備庫并行度
- Mysql5.7.22并行復制策略
18.4月發(fā)布的5.7.22版本、增加一個機遇writeset的并行復制
binlog-transaction-dependency-tracking控制是否使用新的策略
- commit_order、根據同時進入prepare和commit來判斷是否可以并行的策略
- writeset、表示對于事務涉及更新的每一行計算hash值、組成writeset、若兩個事務沒有操作相同行、就可以并行復制
- writeset_session、在主庫上同一個線程先后執(zhí)行的兩個事務、在備庫執(zhí)行時、要保證相同的先后順序、hash值是通過 db+table+index+value計算的、類似5.5按行復制
但: 1. writeset是在主庫生成后直接寫入binlog的、在備庫執(zhí)行時、不需要解析binlog、節(jié)省計算量
- 不需要把整個事務的binlog都掃一遍、才決定分發(fā)到哪個worker、更省內存
- 備庫的分發(fā)策略不依賴binlog內容、binlog格式可以不局限于row
通用性更有保證.
思考: 假設5.7.22版本的主庫、單線程插入很多數(shù)據、過了3個小時、想給這個主庫搭一個相同版本的備庫. 為了更快的追上主庫、需要怎么選擇并行復制策略呢 ?
- commit_order 由于是單線程執(zhí)行、每個事務的commit_id不同、從庫也只能是單線程執(zhí)行
2、 writeset_order 要求同一個線程的日志必須要與主庫上的先后順序相同、也會導致退化為單線程
所以應該選擇 writeset 并行復制策略.