一主多從的設置主要用來讀寫分離,主庫負責所有的寫入和一部分讀,其他的讀請求由從庫承擔。
其中A'和A還互為主備庫,當主庫A發(fā)生故障時,A'會成為新的主庫,此時從庫B和C需要改到同步A'。一般這種都會有專門的系統(tǒng)完成,我們可以看一下這種專門的系統(tǒng)大體有哪幾種方式完成主備切換。
主備切換的方式有幾種?
- 基于位點的主備切換
- 基于GTID的主備切換
如何設置節(jié)點B成為A'的主庫?
需要在節(jié)點B上執(zhí)行以下命令:
-- master_host:主庫A'的IP
-- master_port:主庫A'的端口
-- master_user:用戶名
-- master_password:密碼
-- master_log_file:從庫需要從哪個文件開始同步
-- master_log_pos:從庫需要從日志文件的哪個偏移量開始同步
change master to master_host=$host_name , master_port = $port, master_user = $user_name, master_password = $password, master_log_file = $master_log_name, master_log_pos = $master_log_pos;
節(jié)點B在發(fā)生切換前是A的從庫,因此本地記錄的也是A的位點,但是相同的日志,在A和A'的位點是不同的。因此在切換前,需要找到同步位點。
如何找同步位點?
- 等待節(jié)點A'把中轉(zhuǎn)日志全部同步完成
- 在A'上執(zhí)行show master status,得到A'上最新的File和Position
- 取主庫A故障的時刻T
- 用MySQLbinlog工具解析A'的File,得到T時刻的位點
mysqlbinlog file --stop-datetime=T --start-dateTime=T
上圖中,end_log_pos后面的123表示的A'實例在故障時刻T寫入新的binlog的位置,此位置用在節(jié)點B的的change master指令中。
基于位點主備切換的弊端?
無法精準的找出同步位置,在上面的找的位置我們是不準確的,假設有一種情況,主庫A在執(zhí)行一條insert語句以后插入了一行數(shù)據(jù)R,并且已經(jīng)將binlog傳給了A'和B,此時A(T時刻)發(fā)生宕機,此時系統(tǒng)的狀態(tài)如下:
- 從庫B,由于同步了binlog,R這一行會被插入
- 在A'上,R這一行也會存在,但是日志是寫在T時刻以后
此時如果們在庫B上執(zhí)行change master命令,從T時刻的position開始同步,就會把插入R這一行的binlog再次同步到從庫執(zhí)行,此時從庫B的同步線程會因主鍵沖突而停止同步。
如何暴力解決上述錯誤?
- 主動跳過一個事務
- 主動跳過指定錯誤
如何主動跳過一個事務?
-- 每次遇到錯誤都要執(zhí)行一次sql_slave_skip_counter
set global sql_slave_skip_counter=1;
start slave;
如何跳過指定錯誤?
mysql主要有很多錯誤類型,如下兩種:
- 1062:插入數(shù)據(jù)時唯一鍵沖突
- 1032:刪除數(shù)據(jù)時找不到行
我們可以在mysql配置文件中添加以下內(nèi)容:
slave_skip_errors=1062,1032
或者在啟動的時候增加啟動參數(shù)--slave_skip_errors=1062,1032。
等主備同步關系建立完成以后并且穩(wěn)定執(zhí)行一段時間,我們再還原參數(shù),避免后續(xù)的問題。
什么是GTID?
GTID(Global Transaction Identifier)全稱是全局事務ID,是一個事務在提交的時候生成的,是這個事務唯一的表示,格式如下:
GTID=server_uuid:gno
- server_uuid:實例第一次啟動時自動生成,全局唯一的值
- gno:初始值為1,每次提交事務的時候分配給這個事務,并加1
如何啟動GTID?
# 在配置文件中增加下面兩行參數(shù)
gtid_mode = on
enforce_gtid_consistency = on
-- 查看GTID相關參數(shù)
show variables like "%gtid%";
GTID的生成方式?
GTID有兩種生成方式,使用哪種方式取決于Session變量gtid_next的值:
- gtid_next=automatic:使用默認值,mysql會將server_uuid:gno分配給此事務
- gtid_next是指定的值:比如通過set gtid_nex='current_gtid'指定
每個MySQL實例都維護了一個GTID集合,用來對應這個實例執(zhí)行過的所有事務。
GTID automatic
gtid使用默認值時:
- 記錄binlog的時候,會先記錄一行set @@session.gtid_next='server_uuid:gno'
- 將改GTID加入本實例的GTID集合
GTID current_gtid
gtid使用指定值時:
- 如果current_gtid已經(jīng)存在于實例的GTID集合,該事務會被忽略
- 如果current_gtid不存在于實例的GTID集合,就將current_gtid分配給接下來要執(zhí)行的事務,也就是說系統(tǒng)不需要生成新的GTID
一個current_gtid只能給一個事務使用。如果要執(zhí)行下一個事務必須使用另一個唯一的gtid或者變成automatic。
基于GTID的主備切換
-- master_host:主庫A'的IP
-- master_port:主庫A'的端口
-- master_user:用戶名
-- master_password:密碼
change master to master_host=$host_name , master_port = $port, master_user = $user_name, master_password = $password, master_auto_position=1
master_auto_position=1表示主備關系使用的是GTID協(xié)議,假設當前時刻下,節(jié)點A'的GTID集合是set_a,實例B的GTID集合是set_b,我們在B上執(zhí)行start slave指令以后,節(jié)點B對binlog的處理邏輯如下:
- 節(jié)點B指定主庫A',基于主備協(xié)議建立連接
- 節(jié)點B把set_b發(fā)送給主庫A'
- 節(jié)點A'計算出set_b和set_a的差集(在set_a但不存在與set_b的GTID集合),判斷A'是否包含了這個差集所需要的所有binlog事務:如果不包含,表示A'已經(jīng)把實例B需要的binlog刪掉了,直接返回錯誤;如果確認包含,A'從自己的binlog文件中找出第一個不存在set_b的事務發(fā)送給B
- 之后就從這個事務開始,往后讀取文件,按順序取binlog發(fā)送給B執(zhí)行