共計 5153 個字符,預計需要花費 13 分鐘才能閱讀完成。
這篇文章給大家介紹如何理解 MySQL 多線程復制,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
Enhanced Multi-threaded Slaves
首先梳理下傳統 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 線程一樣的功能來重放在主庫執行的事務?答案當然是:可以!但是我們需要解決以下問題:
1、slave 本地的 relay
log 記錄的是 master 的 binary log 日志信息,日志記錄的信息按照事務的時間先后順序記錄,那么為了保證主備數據一致性,slave 節點必須按照同樣的順序執行,如果順序不一致容易造成主備庫數據不一致的風險。
如:
在 master 節點提交 T1 和 T2 事務按照以下順序
1. State0: x= 1, y= 1
2. T1: {x:= Read(y);
3.
x:= x+1;
4.
Write(x);
5.
Commit; }
6.
State1: x= 2, y= 1
7. T2: {y:= Read(x);
8.
y:=y+1;
9.
Write(y);
10.
Commit; }
11.
State2: x= 2, y= 3
slave 節點執行 T1 和 T2 相反的順序:
1. State0: x= 1, y= 1
2. T2: {y:= Read(x);
3.
y:= y+1;
4.
Write(y);
5.
Commit; }
6.
State1: x= 1, y= 2
7. T1: {x:= Read(y);
8.
x:=x+1;
9.
Write(x);
10.
Commit; }
11.
State2: x= 3, y= 2
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 中主庫提交的事務)
1. +—–+————-+———–+——+———+——-+——————————————————–+——————+
2. | Id | User | Host | db | Command | Time | State
| Info |
3. +—–+————-+———–+——+———+——-+——————————————————–+——————+
4. | 1 | system
user | | NULL | Connect | 29923 | Slave has read all relay log; waiting for more updates | NULL |
5. | 2 | system
user | | NULL | Connect | 29923 | Waiting for an event
from Coordinator
| NULL
|
6. | 3 | system
user | | NULL | Connect | 29923 | Waiting for an event
from Coordinator
| NULL
|
7. | 4 | system
user | | NULL | Connect | 29923 | Waiting for an event
from Coordinator
| NULL
|
問題 2:
MySQL
5.6 基于 schema 級別的并發復制能夠解決當業務數據的表放在不同的 database 庫下,但是實際生產中往往大多數或者全部的業務數據表都放在同一個 schema 下,在這種場景即使 slave_parallel_workers 0 設置也無法并發執行 relay
log 中記錄的主庫提交數據。高并發的情況下,由于 slave 無法并發執行同個 schema 下的業務數據表,依然會造成主備延遲的情況。
思考 2:
那么如果 slave 同時可以用多線程的方式,同時執行一個 schema 下的所有業務數據表,將能大大提高 slave 節點執行 ralay log 中記錄的主庫提交事務達到與主庫數據同步的目的,實現該功能我們需要解決什么問題?
1、前面提到過為了保證主庫數據一致性,master 節點寫入的 binary log 日志按照數據庫邏輯時間先后的順序并且 slave 節點執行 relay log 中主庫提交的事務必須按照一致的順序否則會造成主備數據不一致的情況。
2、既然要實現 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
2.
3. #151223 15:11:28 server id 15102
end_log_pos 15199 CRC32 0x7dd1bf05 GTID last_committed=26 sequence_number=27
4.
5. #151223 15:11:28 server id 15102
end_log_pos 15773 CRC32 0xb01dc76e GTID last_committed=26 sequence_number=28
6.
7. #151223 15:11:28 server id 15102
end_log_pos 16347 CRC32 0x7a8e0ee8 GTID last_committed=26 sequence_number=29
8.
9. #151223 15:11:28 server id 15102
end_log_pos 16921 CRC32 0x92516d17 GTID last_committed=26 sequence_number=30
10.
11. #151223 15:11:28 server id 15102
end_log_pos 17495 CRC32 0xeb14a51e GTID last_committed=26 sequence_number=31
12.
13. #151223 15:11:28 server id 15102
end_log_pos 18071 CRC32 0x750667d0 GTID last_committed=26 sequence_number=32
14.
15. #151223 15:11:28 server id 15102 end_log_pos
18645 CRC32 0xcaed6159 GTID last_committed=26 sequence_number=33
16.
17. #151223 15:11:28 server id 15102
end_log_pos 19219 CRC32 0x62408408 GTID last_committed=26 sequence_number=34
18.
19. #151223 15:11:28 server id 15102
end_log_pos 19793 CRC32 0x5cf46239 GTID last_committed=33 sequence_number=35
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
關于如何理解 MySQL 多線程復制就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。