一主多從的設置,用于讀寫分離,主庫負責所有的寫入和一部分讀,其他讀請求則由從庫分擔。
一主多從架構下,主庫故障后的主備切換問題。相比于一主一備,多了從庫指向新主庫的過程。
基于位點的主備切換同步
把節點B設置為節點A‘的從庫,執行change master
命令:
CHANGE MASTER TO
MASTER_HOST = $host_name
MASTER_POST = $port
MASTER_USER = $user_name
MASTER_PASSWORD = $password
MASTER_LOG_FILE = $master_log_name
MASTER_LOG_POS = $master_log_pos
-
MASTER_HOST、MASTER_PORT、MASTER_USER和MASTER_PASSWORD四個參數,分別達標主庫A’的IP、端口、用戶名、密碼
-
MASTER_LOG_FILE和MASTER_LOG_POS是主庫對應的文件名和日志偏移量,就是同步位點
節點B要設置為A‘的從庫,就必須要設置位點參數。
原本節點B是A的從庫,本地記錄的也是A的位點。 但是相同的日志,A的位點于A’的位點不同。所以在切換的時候需要先找同步位點。
(這個不一致造成的原因之一:備庫啟用了并行復制,例如使用了組提交并行(prepare和commit狀態之間事務組可以并行執行),那么這時候在主庫上先執行完成的事務在備庫上就不一定先執行完成了,binlog因此就會有所差異。 )
找同步位點的方法:
1、等待新主庫A‘把中轉日志(relay log)全部同步完成
2、在A’上執行show master status
命令,得到當前A’上最新的file和position
3、取原主庫A故障時刻T
4、用mysqlbinlog
工具解析A’的file,得到T時刻的位點123
不過這樣并不精確:
假設在T時刻,主庫A已經執行完成了一個insert語句插入一行數據R,并且已經將binlog傳給A‘和B,然后在傳完的瞬間主庫A的主機就掉電了。
此時系統狀態如下:
1、在從庫B上,由于同步了binlog,R這一行已經存在
2、在新主庫A’上,R這一行也已經存在,日志卸載123位置后
3、在從庫B上執行change master
命令,指向A’的file文件123位置,會把插入R這一行數據的binlog又同步到從庫B執行,會發生主鍵沖突,然后停止同步
所以在切換主庫的時候要主動跳過一些"重復操作"引起的錯誤,避免切換任務阻塞。
1、在從庫上執行跳過事務命令:
set global sql_slave_skip_counter = 1;
start slave;
每次碰到錯誤就停下來,執行以此跳過命令,直到不再報錯。
2、通過設置slave_skip_errors
參數,直接設定跳過指定的錯誤
主備切換時,有兩類錯誤經常遇到:
1062 插入數據時唯一鍵沖突
1032 刪除數據時找不到行
可以把slave_skip_errors
設置為"1032,1062",遇到這兩個錯誤直接跳過。
注意,在主備切換過程中,跳過這兩個錯誤是無損的。同步完成后,需要把整個參數設置為空,防止出現主從不一致時也跳過了。
基于GTID的主備切換同步
GTID全稱Global Transaction Identifier ,也就是全局事務ID,是一個事務在提交的時候生成的,是這個事務的唯一標識。格式為:
GTID = server_uuid:gno
server_uuid
是一個實例第一次啟動時自動生成的,是一個全局唯一的值- gno是一個整數,初始值為1,每次提交事務的時候分配給這個事務,并+1
官方格式定義:
GTID = source_id:transaction_id
transaction_id指的是事務id,事務id實在事務執行的過程中分配的,如果這個事務回滾了,transaction_id也會增加,而gno實在事務提交的時候才會分分配,所以使用gno更好。
使用方法:在啟動一個MySQL實例的時候,加上參數gtid_mode = on
和enforce_gtid_consistency = on
。
在GTID模式下,每個事務都會和一個GTID對應。
在GTID模式下,備庫B要設置為新主庫A’的從庫:
CHANGE MASTER TO
MASTER_HOST=$host_name
MASTER_PORT=$port
MASTER_USER=$user_name
MASTER_PASSWORD=$password
master_auto_position=1
可以發現,現在不需要指定參數了。
實例A‘的GTID集合記為set_a,實例B的GTID集合記為set_b。
在實例B上執行start slave命令,取binlog:
1、實例B指定主庫A’,基于主備協議建立連接
2、實例B把set_b發給主庫A‘
3、實例A’算出set_a與set_b的差集,也就是所有存在于set_a但是不存在于set_b的GTID集合。
判斷A‘本地是否包含了這個差集需要的binlog事務。
如果不包含,表示A’已經把實例B需要的binlog刪除了,直接返回錯誤
如果確認全部包含,A‘從自己的binlog文件里面,找出第一個不在set_b的事務,發給B
4、從這個事務開始,往后讀文件,按順序取binlog發給B去執行
也就是說,基于GTID的主備關系里,系統認為只要建立主備關系,就必須保證主庫發給備庫的日志是完整的。如果備庫要求的日志不存在,主庫就拒絕把日志發給備庫。
基于位點的主備協議,是由備庫決定的,備庫指定位點,主庫順著位點取log,不做日志完整性判斷。