主從模式原理
1.1 概念:MySQL主從復制是指數據可以從一個MySQL數據庫服務器主節點復制到一個或多個從節點。MySQL 默認采用異步復制方式,這樣從節點不用一直訪問主服務器來更新自己的數據,數據的更新可以在遠程連接上進行,從節點可以復制主數據庫中的所有數據庫或者特定的數據庫,或者特定的表。
1.2 主要用途:
1.在開發工作中,有時候會遇見某個sql 語句需要鎖表,導致暫時不能使用讀的服務,這樣就會影響現有業務,使用主從復制,讓主庫負責寫,從庫負責讀,這樣,即使主庫出現了鎖表的情景,通過讀從庫也可以保證業務的正常運作。
2.數據實時備份,當系統中某個節點發生故障時,可以方便的故障切換(主從切換)
3.高可用(HA)
4.隨着系統中業務訪問量的增大,如果是單機部署數據庫,就會導致I/O訪問頻率過高。有了主從復制,增加多個數據存儲節點,將負載分布在多個從節點上,降低單機磁盤I/O訪問的頻率,提高單個機器的I/O性能。
1.3 MySQL主從形式
1.一主多從(實現HA,讀寫分離,提供集群的並發能力)
2.多主一從(從庫主要用於數據庫備份作用)
3.雙主復制
4.級聯復制(級聯復制模式下,部分slave的數據同步不連接主節點,而是連接從節點。因為如果主節點有太多的從節點,就會損耗一部分性能用於replication(復制),那么我們可以讓3~5個從節點連接主節點,其它從節點作為二級或者三級與從節點連接,這樣不僅可以緩解主節點的壓力,並且對數據一致性沒有負面影響。級聯復制下從節點也要開啟binary log(bin-log)功能)
1.4 MySQL主從復制的原理(重點)
MySQL主從復制涉及到三個線程,一個運行在主節點(log dump thread),其余兩個(I/O thread, SQL thread)運行在從節點,如下圖所示:
1. 主節點log dump線程
當從節點連接主節點時,主節點會為其創建一個log dump 線程,用於發送和讀取bin-log的內容。在讀取bin-log中的操作時,log dump線程會對主節點上的bin-log加鎖,當讀取完成,在發送給從節點之前,鎖會被釋放。主節點會為自己的每一個從節點創建一個log dump 線程。
2.從節點I/O線程
當從節點上執行start slave
命令之后,從節點會創建一個I/O線程用來連接主節點,請求主庫中更新的bin-log。I/O線程接收到主節點的blog dump進程發來的更新之后,保存在本地relay-log(中繼日志)中。
3.從節點SQL線程
SQL線程負責讀取relay-log中的內容,解析成具體的操作並執行,最終保證主從數據的一致性。
對於每一個主從連接,都需要這三個進程來完成。當主節點有多個從節點時,主節點會為每一個當前連接的從節點建一個log dump 進程,而每個從節點都有自己的I/O進程,SQL進程。從節點用兩個線程將從主庫拉取更新和執行分成獨立的任務,這樣在執行同步數據任務的時候,不會降低讀操作的性能。比如,如果從節點沒有運行,此時I/O進程可以很快從主節點獲取更新,盡管SQL進程還沒有執行。如果在SQL進程執行之前從節點服務停止,至少I/O進程已經從主節點拉取到了最新的變更並且保存在本地relay日志中,當服務再次起來之后,就可以完成數據的同步。
要實施復制,首先必須打開Master 端的binary log(bin-log)功能,否則無法實現。
因為整個復制過程實際上就是Slave 從Master 端獲取該日志然后再在自己身上完全順序的執行日志中所記錄的各種操作。如下圖所示:
1.5 MySQL主從復制的模式(理解)
MySQL 主從復制默認是異步的模式。MySQL增刪改操作會全部記錄在bin-log(binary log)中,當slave節點連接master時,會主動從master處獲取最新的bin-log文件。並把bin-log存儲到本地的relay-log中,然后去執行relay-log的更新內容。
主要有三種模式:
1. 異步模式
異步模式如下圖所示,這種模式下,主節點不會主動推送bin-log到從節點,主庫在執行完客戶端提交的事務后會立即將結果返給給客戶端,並不關心從庫是否已經接收並處理,這樣就會有一個問題,主節點如果崩潰掉了,此時主節點上已經提交的事務可能並沒有傳到從節點上,如果此時,強行將從提升為主,可能導致新主節點上的數據不完整。
2.半同步模式(mysql semi-sync)
介於異步復制和全同步復制之間,主庫在執行完客戶端提交的事務后不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到relay-log中才返回成功信息給客戶端(只能保證主庫的bin-log至少傳輸到了一個從節點上,但並不能保證從節點將此事務執行更新到db中),否則需要等待直到超時時間然后切換成異步模式再提交。相對於異步復制,半同步復制提高了數據的安全性,一定程度的保證了數據能成功備份到從庫,同時它也造成了一定程度的延遲,但是比全同步模式延遲要低,這個延遲最少是一個TCP/IP往返的時間。所以,半同步復制最好在低延時的網絡中使用。如下圖所示:
3.全同步模式
指當主庫執行完一個事務,然后所有的從庫都復制了該事務並成功執行完才返回成功信息給客戶端。因為需要等待所有從庫執行完該事務才能返回成功信息,所以全同步復制的性能必然會收到嚴重的影響。
1.6 GTID復制模式
在傳統的復制里面,當發生故障,需要主從切換,需要找到bin-log和pos點(指從庫更新到了主庫bin-log的哪個位置,這個位置之前都已經更顯完畢,這個位置之后未更新),然后將主節點指向新的主節點,相對來說比較麻煩,也容易出錯。在MySQL 5.6里面,不用再找bin-log和pos點,我們只需要知道主節點的ip,端口,以及賬號密碼就行,因為復制是自動的,MySQL會通過內部機制GTID自動找點同步。
基於GTID的復制是MySQL 5.6后新增的復制方式.
GTID (global transaction identifier) 即全局事務ID, 保證了在每個在主庫上提交的事務在集群中有一個唯一的ID.
1.7 MySQL主從復制的方式(重點)
MySQL主從復制有三種方式:基於SQL語句的復制(statement-based replication, SBR) , 基於行的復制(row-based replication, RBR) , 混合模式復制(mixed-based replication, MBR)。對應的bin-log文件的格式也有三種:STATEMENT, ROW , MIXED
Statement-base Replication (SBR)
就是記錄sql語句在bin-log中,MySQL 5.1.4及之前的版本都是使用這種復制格式。優點就是只需要記錄會修改數據的sql語句到bin-log中,減少了bin-log日質量,節約I/O,提高性能。缺點是在某些情況下,會導致主從節點中數據不一致(比如sleep(),now()或自定義函數存儲過程沒同步,導致執行失敗等)
Row-based Relication(RBR)
mysql master將SQL語句分解為基於Row更改的語句並記錄在bin-log中,也就是只記錄哪條數據被修改了,修改成什么樣。優點是不會出現某些特定情況下的存儲過程、或者函數、或者trigger的調用或者觸發無法被正確復制的問題。缺點是會產生大量的日志,尤其是修改table的時候會讓日志暴增,同時增加bin-log同步時間。也不能通過bin-log解析獲取執行過的sql語句,只能看到發生的data變更。
Mixed-format Replication(MBR)
MySQL NDB cluster 7.3和7.4使用的MBR。是以上兩種模式的混合,對於一般的復制使用STATEMENT模式保存到bin-log,對於STATEMENT模式無法復制的操作則使用ROW模式來保存,MySQL會根據執行的SQL語句選擇日志保存方式。
配置主從模式
2.1 docker下mysql創建
docker run -d name mysql_master -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --network bridge --ip 172.17.0.2 mysql:5.7
docker run -d name mysql_slave -p 3307:3306 -e MYSQL_ROOT_PASSWORD=123456 --network bridge --ip 172.17.0.3 mysql:5.7
這里如果第一步ip沒有指定,docker容器每次啟動ip會自動變化,可以另外添加另一個網絡環境
docker network create --subnet 172.18.0.0/16 my-net
docker network connect --ip 172.18.0.2 my-net mysql_master
docker network connect --ip 172.18.0.3 my-net mysql_slave
如果兩個mysql不是新創建的或者數據不一致,注意先用數據庫同步工具先同步好兩者數據結構、數據一致,再執行主從配置;
2.2 創建賬號和開啟binlog
// 進入master容器
docker exec -it mysql_master /bin/bash
// 登錄mysql
mysl -u root -p
// 創建賬號
GRANT REPLICATION SLAVE ON *.* to 'slave'@'172.18.0.%' identified by '123456';
flush privileges;
修改mysqld配置,注意docker 5.7下配置路徑為:/etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]# 開啟binlog
log-bin=mysql-bin
server-id=1
binlog_format=mixed
# 需要同步的數據庫,如果不配置則同步全部數據庫
binlog-do-db=db_test
# binlog日志保留的天數,清除超過10天的日志
# 防止日志文件過大,導致磁盤空間不足
expire-logs-days=10
# 事務提交過程中的數據同步模式控制
innodb_flush_log_at_trx_commit=1
配置完成后,重啟mysql
docker restart mysql_master
重新進入mysql中,查看當前binlog文件狀態(后面slave配置會用到File、Position參數)
mysql> flush logs;
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000005 | 154 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
2.3 Slave配置binlog
修改mysqld配置,注意docker 5.7下配置路徑為:/etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
server-id=2
log-bin=mysql-bin
innodb_flush_log_at_trx_commit=1
sync_binlog=1
進入mysql,配置(注意步驟2.2中到File、Position參數):
change master to master_host='172.18.0.2',master_user='slave',master_password='123456',master_port=3306,master_log_file='mysql-bin.000005',master_log_pos=154,master_connect_retry=30;
// 啟動slave服務
start slave;
// 查看slave狀態:
show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.18.0.2
Master_User: slave
Master_Port: 3306
Connect_Retry: 30
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 491
Relay_Log_File: 1e73c1ca86e3-relay-bin.000002
Relay_Log_Pos: 657
Relay_Master_Log_File: mysql-bin.000005
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 491
Relay_Log_Space: 871
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 84868f03-4366-11ec-80e7-0242ac110007
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
ERROR:
No query specified
Slave_IO_Running、Slave_SQL_Running為Yes,Last_IO_Error沒有報錯,說明主從配置成功了,之后可以主mysql實現創建一個表並插入一條數據,看看slave數據庫是否同步了;
參考資料:【MySQL】主從復制實現原理詳解