MySQL主从复制原理学习
一、主從復制過程
master 服務器將數(shù)據(jù)的改變記錄二進制 binlog 日志,當 master 上的數(shù)據(jù)發(fā)生改變時,則將其改變寫入二進制日志中;
slave 服務器會在一定時間間隔內(nèi)對 master 二進制日志進行探測其是否發(fā)生改變,如果發(fā)生改變,則開始一個 I/OThread 請求 master 二進制事件;
同時主節(jié)點為每個 I/O 線程啟動一個 dump 線程,用于向其發(fā)送二進制事件,并保存至從節(jié)點本地的中繼日志中,從節(jié)點將啟動 SQL 線程從中繼日志中讀取二進制日志,在本地重放,使得其數(shù)據(jù)和主節(jié)點的保持一致,最后 I/OThread 和 SQLThread 將進入睡眠狀態(tài),等待下一次被喚醒。
1.1 binlog
主從復制是在binlog的基礎上進行的
binlog格式可以設置為:statement、row、mixed三種
- Statement 基于語句,只記錄對數(shù)據(jù)做了修改的SQL語句,能夠有效的減少binlog的數(shù)據(jù)量,提高讀取、基于binlog重放的性能
- Row 只記錄被修改的行,所以Row記錄的binlog日志量一般來說會比Statement格式要多。基于Row的binlog日志非常完整、清晰,記錄了所有數(shù)據(jù)的變動,但是缺點是可能會非常多,例如一條update語句,有可能是所有的數(shù)據(jù)都有修改;再例如alter table之類的,修改了某個字段,同樣的每條記錄都有改動。
- Mixed Statement和Row的結合,怎么個結合法呢。例如像update或者alter table之類的語句修改,采用Statement格式。其余的對數(shù)據(jù)的修改例如update和delete采用Row格式進行記錄。
1.2 relaylog
二、異步復制、半同步復制
2.1 slave io_thread
1.根據(jù)master的ip和port,slave_io線程連接到master,如果連接不上,會發(fā)起重試
間隔時間:master_connect_retry(默認 60s)
2.發(fā)送sql命令 設置主庫master_heartbeat_period(默認slave_net_timeout/2)以及計算主從時間差 clock_diff_with_master(主從機器的系統(tǒng)時間差+網(wǎng)絡延遲)
3.io線程發(fā)送com_binlog_dump協(xié)議請求
4.waiting for master to send event
5.queueing master event to the relay log喚醒sql線程
6.修改master log pos的值 flush master_info(FLUSH頻率為sync_master_info的值)將主庫信息寫入master_log_info表
7.繼續(xù)步驟3 循環(huán)
2.2 主庫binlog_dump流程
1.DUMP線程收到COM BINLOG DUMP報文,解析上送filename和pos,并設置心跳包間隔時間master heartbeat period以及發(fā)送ROTATE EVENT更新從庫的masterlog_name
2.進入"Sending binlog event to slave"狀態(tài),準備發(fā)送event到SlavelO線程
3.獲取讀取的binlog的尾指針位置,如果已達到hot binlog即最新的binlog文件)的尾部,則處于"Master has sent all binlog to slave; waiting for more updates"等待主庫產(chǎn)生binlog
4.獲取到尾指針,將相關的event發(fā)送到SlavelO一個文件結束
5.繼續(xù)下個文件執(zhí)行步驟3,如此循環(huán) 純真笑容
2.3 slave_sql線程流程
1.對SlaveSQL線程進行參數(shù)初始化,并設置需要開始讀取relaylog的name和 pos.
2.進入"Reading event from the relay log"狀態(tài),開始讀取event
3.如果沒有新event,進入"Slave has read all relay loq: waiting for more updates"狀態(tài),等待Slave_IO線程寫入新event
4.讀取到event設置lastmastertimestamp以及sql線程的starttime計算主從延時的重要依據(jù))并判斷是否需要跳過event的apply
5.根據(jù)event的數(shù)據(jù)在從庫進行apply應用處理
6.對于非xid的event,更新讀取relaylog的文件指針即event relaylog pos值當一個事務結束時候,會flushinfo(true)將rli信息強制寫入relayloginfo表(默認 relay_log_info_repository=TABLE)
7.再次讀取下個event進入步驟2如此循環(huán) 純真笑容
2.4 半同步復制
2.4.1 sync binlog
-
當sync_binlog為0的時候,binlog sync磁盤由OS負責.
-
當sync_binlog值大于1的時候,sync binlog操作可能并沒有使binlog落盤(需要達到sync_binlog值之后才會進行fsync).如果沒有實際落盤,事務在提交前,Master crash了,Master再次啟動后原事務就會被回滾。但可能Dump線程已將events同步到Slave,并且Slave已經(jīng)應用了這些events,這也會導致Slave數(shù)據(jù)比Master多,主備同步失敗。
2.4.2 sync relay log
- 參數(shù)解釋
當sync_relay_log為0的時候,relaylog sync磁盤由OS負責.
當sync_relay_log>1的時候,semisync返回給Master的position可能沒有fsync到磁盤.
sync_relay_log=1的時候,會保證semisync返回給Master的positiony已經(jīng)fsync到磁盤.
- 異常舉例:
在gtid_mode下,在sync_binlog=1的情況下,sync_relay_log不是1的時候,僅發(fā)生Master或Slave的一次Crash并不會發(fā)生數(shù)據(jù)丟失或者主備同步失敗情況。
如果發(fā)生Slave沒有sync relay log,Master端事務提交,然后Slave端Crash。這樣Slave端就會丟失掉已經(jīng)回復Master ACK的事務events。
但當Slave再次啟動,如果沒有來得及從Master端同步丟失的事務Events,Master就Crash。這個時候,用戶訪問Slave就會發(fā)現(xiàn)數(shù)據(jù)丟失.
如果完全要保證半同步的主從數(shù)據(jù)的一致性需要設置sync_binlog和sync_relay_log都為1,但是這會帶來性能的瓶頸(尤其是sync_relay_log為1 每同步一個event就需要fsync).所以實際的應用中,會根據(jù)數(shù)據(jù)一致性和性能的綜合考慮設置合理的值.
三、常見主從報錯與解決方式
3.1 Last error主從數(shù)據(jù)不一致
查看導致last error產(chǎn)生的信息
#方法1——通過binlog查詢 show binlog events in 'mysql-bin.032102' from 730019106 limit 10; #找到對應行,該行中Info信息就是1973位置所做操作 #方法2——通過ps表查詢 select * from performance_schema.replication_applier_status_by_worker where LAST_ERROR_NUMBER=1396如果確認可以跳過或者短時間無法解決
# 跳過指定數(shù)量的事務,通常為1跳過當前出錯事務 mysql > stop slave ; mysql > set global sql_slave_skip_counter=1 mysql > start slave ;# 跳過指定類型的錯誤或者所有錯誤 vi /etc/my.cnf [mysqld] slave-skip-errors=1062,1146,2341 #跳過指定錯誤類型 slave-skip-errors=all #跳過所有錯誤,不建議使用# GTID模式下跳過事務 stop slave; set gtid_next='fb6d07d2-a253-1212-b2fh-29255eg3f3g:23' #show slave status信息中retrieved_gtid_set里的gtid為 fb6d07d2-a253-1212-b2fh-29255eg3f3g:18-23 begin;commit; #手動指定gtid_next,如果gtid已經(jīng)存在于實例的GTID集合中,該事務會被忽略;如果沒有存在于GTID集合中就將這個gtid分配給接下來要執(zhí)行的事務,系統(tǒng)不需要給這個事務生成新的GTID set gtid_next='AUTOMATIC'; #修改回自動獲取GTID start slave;# 使用gtid_purged跳過事務 show slave status \G; #先查看executed_gtid_set的值,該值有兩行,看下面那行 show variables like '%gtid_purged%' #查看主庫purged值,假設末尾是1-33 reset master #清空從庫binlog和gtid_executed狀態(tài) set global gtid_purged='xxxxxxxxxxxxxxxxxxxxxxxxx:1-33'; 跳過1-33的事務 start slave; #主從狀態(tài)恢復后需手動補跳過的事務所產(chǎn)生的數(shù)據(jù)3.2 連接主庫報錯
Last_IO_Errno: 1040 Last_IO_Error: error reconnecting to master 'repl@10.0.0.51:3307' - retry-time: 10 retries: 7通過復制用戶 手動連接
判斷是否是主庫連接數(shù)過多還是防火墻不同
3.3 relay log損壞
Last_SQL_Error: Error initializing relay log position: I/O error reading the header from the binary log Last_SQL_Error: Error initializing relay log position: Binlog has bad magic number; It's not a binary log file that can be used by this version of MySQL找到同步的binlog和POS點,然后重新做同步,這樣就可以有新的relaylog
Relay_Master_Log_File: mysql-bin.000010 Exec_Master_Log_Pos: 821 mysql> stop slave; Query OK, 0 rows affected (0.01 sec) mysql> CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000010',MASTER_LOG_POS=821; Query OK, 0 rows affected (0.01 sec) mysql> start slave;3.4 主庫crash 從庫重復回放
在開啟 GTID 模式下,如果指定 master_auto_position=1,start slave 時,從庫會把 Retrieved_Gtid_Set 和 Executed_Gtid_Set 的并集發(fā)送給主庫,主庫將收到的并集和自己的 gtid_executed 比較,把從庫 GTID 集合里缺失的事務全都發(fā)送給從庫。
主機重啟后,事務重復回放,表明 Retrieved_Gtid_Set 和 Executed_Gtid_Set 的并集中有 GTID 事務丟失,導致重復獲取事務執(zhí)行引發(fā)主鍵沖突錯誤。
Retrieved_Gtid_Set 和 Executed_Gtid_Set 均為內(nèi)存變量,MySQL 重啟后,Retrieved_Gtid_Set 初始化為空值,從而推斷出 Executed_Gtid_Set 有 GTID 事務丟失。
Executed_Gtid_Set 來源于 gtid_executed 變量,gtid_executed 變量持久化介質(zhì)有 mysql.gtid_executed 表和 binlog 日志
當 log_bin=on ,log_slave_updates=on 時,只有在 binlog 切換時侯才會更新 mysql.gtid_executed 表,保存直到上一個 binlog 執(zhí)行過的 GTID 集合。MySQL 重啟后,在默認參數(shù) binlog_gtid_simple_recovery=1 時,gtid_executed 變量值從最后一個 binlog 文件計算獲得
Worker 線程報 1062 主鍵沖突錯誤–> gtid_executed 信息陳舊–> binlog 未實時持久化
3.5 級聯(lián)復制報錯
針對雙主級聯(lián)復制,需要檢查從庫的read_only選項,雙主模式下的自增步長,以及log_slave_updates參數(shù),否則雙主模式下,可能會有問題
3.6 主從切換后gtid不一致
從庫之前執(zhí)行過命令導致產(chǎn)生了新的gtid,并且該gtid所在的binlog已經(jīng)被清理
導致發(fā)生主從切換之后,新從庫同步復制新主庫時,由于master_auto_position=1模式下,從庫會將當前已經(jīng)執(zhí)行過的gtid集合發(fā)送給主庫,主庫接收到從庫發(fā)送的gtid集合后,會與當前已經(jīng)執(zhí)行的gtid set求差值,并將沒有執(zhí)行過的gtid發(fā)送給備庫,由于binlog被清理導致無法將gtid發(fā)送給從庫(gtid_purged沒有包含)
解決:在新從庫手動跳過缺失的gtid(注:跳過缺失的gtid,意味著不在從庫執(zhí)行缺失的事務,可能導致數(shù)據(jù)的丟失)
數(shù)據(jù)恢復:利用主庫備份重做或者利用binlog備份 恢復到原來的日志目錄(需要的binlog要從第一個包含缺失的gtid日志到當前的全部日志)
3.7 多線程復制報錯
對于多線程復制,slave_pending_jobs_size_max變量設置用于保存尚未應用的event的工作隊列可用的最大內(nèi)存量(以字節(jié)為單位)。設置此變量對未啟用多線程處理的復制沒有影響。設置此變量不會立即生效。必須要停掉復制之后,重新start slave。
此變量的最小值為1024;默認值為16MB。最大可能值為18446744073709551615(16 EB)。
此變量的值是軟限制,可以設置為與正常工作負載匹配。如果異常大的事件超過此大小,事務將被保留,直到所有工作線程都有空隊列,然后進行處理。如果內(nèi)存富余,或者延遲較大時,可以適當調(diào)大;注意這個值要比主庫的max_allowed_packet大!
當worker線程正在處理的event的總大小超過slave_pending_jobs_size_max變量的大小時,將發(fā)生此等待操作。此時可有在主庫看到線程的狀態(tài)為:Waiting for Slave Workers to free pending events
總結
以上是生活随笔為你收集整理的MySQL主从复制原理学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 量化交易入门阶段:布林带调整参数又如何?
- 下一篇: 7. 数据的规整:分组、聚合、合并、重塑