MySQL 主從複製原理不再難
阿新 • • 發佈:2020-10-22
上篇我們分析過 Binlog 日誌的作用以及儲存原理,感興趣的可以翻閱:
[一文帶你瞭解 Binlog 日誌](https://www.cnblogs.com/rickiyang/p/13841811.html)
Binlog 日誌主要作用是資料恢復和主從複製。本身就是二進位制格式的日誌檔案,網路傳輸無需進行協議轉換。MySQL 叢集的高可用,負載均衡,讀寫分離等功能都是基於Binlog 來實現的。
#### MySQL 主從複製主流架構模型
我們基於 Binlog 可以複製出一臺 MySQL 伺服器,也可以複製出多臺,取決於我們想實現什麼功能。主流的系統架構有如下幾種方式:
##### 1. 一主一從 / 一主多從
![1](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1mx9n5wj30lu0mwgne.jpg)
一主一從和一主多從是最常見的主從架構方式,一般實現主從配置或者讀寫分離都可以採用這種架構。
如果是一主多從的模式,當 Slave 增加到一定數量時,Slave 對 Master 的負載以及網路頻寬都會成為一個嚴重的問題。
##### 2. 多主一從
![1](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1mt1km7j30m80l4407.jpg)
MySQL 5.7 開始支援多主一從的模式,將多個庫的資料備份到一個庫中儲存。
##### 3. 雙主複製
理論上跟主從一樣,但是兩個MySQL伺服器互做對方的從,任何一方有變更,都會複製對方的資料到自己的資料庫。雙主適用於寫壓力比較大的業務場景,或者 DBA 做維護需要主從切換的場景,通過雙主架構避免了重複搭建從庫的麻煩。(主從相互授權連線,讀取對方binlog日誌並更新到本地資料庫的過程;只要對方資料改變,自己就跟著改變)
##### 4. 級聯複製
![1](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1mr6mxzj30z60hg40u.jpg)
級聯模式下因為涉及到的 slave 節點很多,所以如果都連在 master 上對主伺服器的壓力肯定是不小的。所以部分 slave 節點連線到它上一級的從節點上。這樣就緩解了主伺服器的壓力。
級聯複製解決了一主多從場景下多個從庫複製對主庫的壓力,帶來的弊端就是資料同步延遲比較大。
#### MySQL 主從複製原理
MySQL 主從複製涉及到三個執行緒:
一個在主節點的執行緒:`log dump thread`
從庫會生成兩個執行緒:一個 I/O 執行緒,一個 SQL 執行緒
如下圖所示:
![4](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1ms5ezhj312s0imtb5.jpg)
主庫會生成一個 log dump 執行緒,用來給從庫 I/O 執行緒傳 Binlog 資料。
從庫的 I/O 執行緒會去請求主庫的 Binlog,並將得到的 Binlog 寫到本地的 relay log (中繼日誌)檔案中。
SQL 執行緒,會讀取 relay log 檔案中的日誌,並解析成 SQL 語句逐一執行。
##### 主節點 log dump 執行緒
當從節點連線主節點時,主節點會為其建立一個 log dump 執行緒,用於傳送和讀取 Binlog 的內容。在讀取 Binlog 中的操作時,log dump 執行緒會對主節點上的 Binlog 加鎖;當讀取完成傳送給從節點之前,鎖會被釋放。**主節點會為自己的每一個從節點建立一個 log dump 執行緒**。
##### 從節點I/O執行緒
當從節點上執行`start slave`命令之後,從節點會建立一個 I/O 執行緒用來連線主節點,請求主庫中更新的Binlog。I/O 執行緒接收到主節點的 log dump 程序發來的更新之後,儲存在本地 relay-log(中繼日誌)中。
##### relay log
這裡又引申出一個新的日誌概念。MySQL 進行主主複製或主從複製的時候會在要複製的伺服器下面產生相應的 relay log。
relay log 是怎麼產生的呢?
從伺服器 I/O 執行緒將主伺服器的 Binlog 日誌讀取過來,解析到各類 Events 之後記錄到從伺服器本地檔案,這個檔案就被稱為 relay log。然後 SQL 執行緒會讀取 relay log 日誌的內容並應用到從伺服器,從而使從伺服器和主伺服器的資料保持一致。中繼日誌充當緩衝區,這樣 master 就不必等待 slave 執行完成才傳送下一個事件。
relay log 相關引數查詢:
```mysql
mysql> show variables like '%relay%';
+---------------------------+------------------------------------------------------------+
| Variable_name | Value |
+---------------------------+------------------------------------------------------------+
| max_relay_log_size | 0 |
| relay_log | yangyuedeMacBook-Pro-relay-bin |
| relay_log_basename | /usr/local/mysql/data/yangyuedeMacBook-Pro-relay-bin |
| relay_log_index | /usr/local/mysql/data/yangyuedeMacBook-Pro-relay-bin.index |
| relay_log_info_file | relay-log.info |
| relay_log_info_repository | TABLE |
| relay_log_purge | ON |
| relay_log_recovery | OFF |
| relay_log_space_limit | 0 |
| sync_relay_log | 10000 |
| sync_relay_log_info | 10000 |
+---------------------------+------------------------------------------------------------+
11 rows in set (0.03 sec)
```
**max_relay_log_size**
標記 relay log 允許的最大值,如果該值為 0,則預設值為 max_binlog_size(1G);如果不為 0,則max_relay_log_size 則為最大的 relay_log 檔案大小。
**relay_log_purge**
是否自動清空不再需要中繼日誌時。預設值為1(啟用)。
**relay_log_recovery**
當 slave 從庫宕機後,假如 relay log 損壞了,導致一部分中繼日誌沒有處理,則自動放棄所有未執行的 relay log,並且重新從 master 上獲取日誌,這樣就保證了 relay log 的完整性。預設情況下該功能是關閉的,將 relay_log_recovery 的值設定為 1 時,可在 slave 從庫上開啟該功能,建議開啟。
**relay_log_space_limit**
防止中繼日誌寫滿磁碟,這裡設定中繼日誌最大限額。但此設定存在主庫崩潰,從庫中繼日誌不全的情況,不到萬不得已,**不推薦使用。**
**sync_relay_log**
這個引數和 Binlog 中的 `sync_binlog`作用相同。當設定為 1 時,slave 的 I/O 執行緒每次接收到 master 傳送過來的 Binlog 日誌都要寫入系統緩衝區,然後刷入 relay log 中繼日誌裡,這樣是最安全的,因為在崩潰的時候,你最多會丟失一個事務,但會造成磁碟的大量 I/O。
當設定為 0 時,並不是馬上就刷入中繼日誌裡,而是由作業系統決定何時來寫入,雖然安全性降低了,但減少了大量的磁碟 I/O 操作。這個值預設是 0,可動態修改,建議採用預設值。
**sync_relay_log_info**
當設定為 1 時,slave 的 I/O 執行緒每次接收到 master 傳送過來的 Binlog 日誌都要寫入系統緩衝區,然後刷入 relay-log.info 裡,這樣是最安全的,因為在崩潰的時候,你最多會丟失一個事務,但會造成磁碟的大量 I/O。當設定為 0 時,並不是馬上就刷入 relay-log.info 裡,而是由作業系統決定何時來寫入,雖然安全性降低了,但減少了大量的磁碟 I/O 操作。這個值預設是0,可動態修改,建議採用預設值。
##### 從節點 SQL 執行緒
SQL 執行緒負責讀取 relay log 中的內容,解析成具體的操作並執行,最終保證主從資料的一致性。
對於每一個主從連線,都需要這三個程序來完成。當主節點有多個從節點時,主節點會為每一個當前連線的從節點建一個 log dump 程序,而每個從節點都有自己的 I/O 程序,SQL 程序。
從節點用兩個執行緒將從主庫拉取更新和執行分成獨立的任務,這樣在執行同步資料任務的時候,不會降低讀操作的效能。比如,如果從節點沒有執行,此時 I/O 程序可以很快從主節點獲取更新,儘管 SQL 程序還沒有執行。如果在 SQL 程序執行之前從節點服務停止,至少 I/O 程序已經從主節點拉取到了最新的變更並且儲存在本地 relay log 中,當服務再次起來之後就可以完成資料的同步。
要實施複製,首先必須開啟 Master 端的 Binlog 功能,否則無法實現。
因為整個複製過程實際上就是 Slave 從 Master 端獲取該日誌然後再在自己身上完全順序的執行日誌中所記錄的各種操作。如下圖所示:
![5](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1mvsnp5j31040iyad2.jpg)
##### 複製的基本過程
1. 在從節點上執行 `sart slave` 命令開啟主從複製開關,開始進行主從複製。從節點上的 I/O 程序連線主節點,並請求從指定日誌檔案的指定位置(或者從最開始的日誌)之後的日誌內容。
2. 主節點接收到來自從節點的 I/O 請求後,通過負責複製的 I/O 程序(log Dump Thread)根據請求資訊讀取指定日誌指定位置之後的日誌資訊,返回給從節點。返回資訊中除了日誌所包含的資訊之外,還包括本次返回的資訊的 Binlog file 以及 Binlog position(Binlog 下一個資料讀取位置)。
3. 從節點的 I/O 程序接收到主節點發送過來的日誌內容、日誌檔案及位置點後,將接收到的日誌內容更新到本機的 relay log 檔案(Mysql-relay-bin.xxx)的最末端,並將讀取到的 Binlog檔名和位置儲存到`master-info` 檔案中,以便在下一次讀取的時候能夠清楚的告訴 Master :“ 我需要從哪個 Binlog 的哪個位置開始往後的日誌內容,請發給我”。
4. Slave 的 SQL 執行緒檢測到relay log 中新增加了內容後,會將 relay log 的內容解析成在能夠執行 SQL 語句,然後在本資料庫中按照解析出來的順序執行,並在 `relay log.info` 中記錄當前應用中繼日誌的檔名和位置點。
#### MySQL 基於 Binlog 主從複製的模式介紹
MySQL 主從複製預設是 **非同步的模式**。MySQL增刪改操作會全部記錄在 Binlog 中,當 slave 節點連線 master 時,會主動從 master 處獲取最新的 Binlog 檔案。並把 Binlog 儲存到本地的 relay log 中,然後去執行 relay log 的更新內容。
##### 非同步模式 (async-mode)
非同步模式如下圖所示:
![6](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1mu02wbj31160kk0vj.jpg)
這種模式下,主節點不會主動推送資料到從節點,主庫在執行完客戶端提交的事務後會立即將結果返給給客戶端,並不關心從庫是否已經接收並處理,這樣就會有一個問題,主節點如果崩潰掉了,此時主節點上已經提交的事務可能並沒有傳到從節點上,如果此時,強行將從提升為主,可能導致新主節點上的資料不完整。
##### 半同步模式(semi-sync)
介於非同步複製和全同步複製之間,主庫在執行完客戶端提交的事務後不是立刻返回給客戶端,而是等待至少一個從庫接收到並寫到 relay log 中才返回成功資訊給客戶端(只能保證主庫的 Binlog 至少傳輸到了一個從節點上),否則需要等待直到超時時間然後切換成非同步模式再提交。
![7](https://tva1.sinaimg.cn/large/007S8ZIlgy1gjx1muypwdj31420jg41q.jpg)
相對於非同步複製,半同步複製提高了資料的安全性,一定程度的保證了資料能成功備份到從庫,同時它也造成了一定程度的延遲,但是比全同步模式延遲要低,這個延遲最少是一個 TCP/IP 往返的時間。所以,半同步複製最好在低延時的網路中使用。
半同步模式不是 MySQL 內建的,從 MySQL 5.5 開始整合,需要 master 和 slave 安裝外掛開啟半同步模式。
##### 全同步模式
指當主庫執行完一個事務,然後所有的從庫都複製了該事務併成功執行完才返回成功資訊給客戶端。因為需要等待所有從庫執行完該事務才能返回成功資訊,所以全同步複製的效能必然會收到嚴重的影響。
#### Binlog 複製實戰
配置 my.cnf
```mysql
[mysqld]
log-bin
server-id
gtid_mode=off #禁掉 gtid
```
新增主從複製使用者:
```mysql
grant replication slave on *.* to 'repl'@'%' identified by 'gtidUser';
flush privileges;
```
然後我們新增一個從庫。
接著我們用命令列的方式來載入主庫的 Binlog 到從庫,這裡可以設定指定的 binlog 檔案和位移值。在從庫執行以下命令:
```mysql
mysql>change master to
master_host='192.168.199.117',
master_user='slave',
master_port=7000,
master_password='slavepass',
master_log_file='mysql-bin.000008',
master_log_pos=0;
mysql>start slave;
mysql>show slave status\G;
```
複製過程中如果出現程式碼性錯誤,個人根據錯誤日誌來決定是否要跳過錯誤繼續執行:
```mysql
mysql>stop slave;
mysql>set global sql_slave_skip_counter=1;
```
#### 主從複製可能會出現的問題
**Slave 同步延遲**
因為 Slave 端是通過 I/O thread 單執行緒來實現資料解析入庫;而 Master 端寫 Binlog 由於是順序寫效率很高,當主庫的 TPS 很高的時候,必然 Master 端的寫效率要高過 Slave 端的讀效率,這時候就有同步延遲的問題。
I/O Thread 的同步是基於庫的,即同步幾個庫就會開啟幾個 I/O Thread。
可以通過 `show slave status` 命令檢視 `Seconds_Behind_Master` 的值來看是否出現同步延遲,這個值代表主從同步延遲的時間,值越大說明延遲越嚴重。值為 0 為正常情況,正值表示已經出現延遲,數字越大從庫落後主庫越多。
基於 Binlog 的複製方式肯定有這種問題,MySQL 官方也意識到,單執行緒不如多執行緒強,所以在 MySQL 5.7 版本引入了基於組提交的並行複製(官方稱為Enhanced Multi-threaded Slaves,即MTS),設定引數:
`slave_parallel_workers>0` 即可,並且 `global.slave_parallel_type=‘LOGICAL_CLOCK’`,
即可支援一個 schema(庫) 下,`slave_parallel_workers`個 worker 執行緒併發執行 relay log 中主庫提交的事務。
**其核心思想:**
一個組提交的事務都是可以並行回放(配合binary log group commit);
slave 機器的 relay log 中 last_committed 相同的事務(sequence_num不同)可以併發執行。其中,變數 `slave-parallel-type` 可以有兩個值:
1. DATABASE 預設值,基於庫的並行複製方式
2. LOGICAL_CLOCK,基於組提交的並行複製方式
MySQL 5.7 開啟 MTS 很簡單,只需要在 Slave 從資料庫的 my.cnf 檔案中如下配置即可:
```mysql
# slave
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=8 #一般建議設定4-8,太多的執行緒會增加執行緒之間的同步開銷
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
```
當然多執行緒帶來的並行複製方案也有很多實現難點,比如事務都是有序執行的,如果並行回放會不會存在執行資料錯亂的問題。這些問題就不在本節解釋,大家有興趣可以繼續深究。
#### 新一代主從複製模式 - GTID 複製模式
在傳統的複製裡面,當發生故障,需要**主從切換**,需要找到 Binlog 和 位點資訊,恢復完成資料之後將主節點指向新的主節點。在 MySQL 5.6 裡面,提供了新的資料恢復思路,只需要知道主節點的 IP、埠以及賬號密碼就行,因為複製是自動的,MySQL 會通過內部機制 **GTID** 自動找點同步。
基於 GTID 的複製是 MySQL 5.6.5 後新增的複製方式。
**GTID (global transaction identifier)** 即全域性事務 ID,一個事務對應一個 GTID,保證了在每個在主庫上提交的事務在叢集中有一個唯一的 ID。
##### GTID複製原理
在原來基於日誌的複製中,從庫需要告知主庫要從哪個偏移量進行增量同步, 如果指定錯誤會造成資料的遺漏,從而造成資料的不一致。
而基於 GTID 的複製中,從庫會告知主庫已經執行的事務的 GTID 的值,然後主庫會將所有未執行的事務的 GTID 的列表返回給從庫,並且可以保證同一個事務只在指定的從庫執行一次,**通過全域性的事務 ID 確定從庫要執行的事務的方式代替了以前需要用 Binlog 和 位點確定從庫要執行的事務的方式**。
基於 GTID 的複製過程如下:
1. master 更新資料時,會在事務前產生 GTID,一同記錄到 Binlog 日誌中。
2. slave 端的 I/O 執行緒將變更的 Binlog,寫入到本地的 relay log 中,讀取值是根據`gitd_next變數`,告訴我們 slave 下一個執行哪個 GTID。
3. SQL 執行緒從 relay log 中獲取 GTID,然後對比 slave 端的 Binlog 是否有記錄。如果有記錄,說明該 GTID 的事務已經執行,slave 會忽略。
4. 如果沒有記錄,slave 就會從 relay log 中執行該 GTID 的事務,並記錄到 Binlog。
5. 在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,如果沒有二級索引就用全部掃描。
##### GTID 組成
GTID = source_id:transaction_id
`source_id` 正常即是 `server_uuid`,在第一次啟動時生成(函式 `generate_server_uuid`),並持久化到 `DATADIR/auto.cnf` 檔案裡。
`transaction_id` 是`順序化的序列號`(sequence number),在每臺 MySQL 伺服器上都是從 1 開始自增長的序列,是事務的唯一標識。
##### GTID 生成
GTID 的生成受 `gtid_next` 控制。
在 Master 上,`gtid_next` 是預設的 `AUTOMATIC`,即 GTID 在每次事務提交時自動生成。它從當前已執行的 GTID 集合(即 gtid_executed)中,找一個大於 0 的未使用的最小值作為下個事務 GTID。在實際的更新事務記錄之前將 GTID 寫入到 Binlog。
在 Slave 上,從 Binlog 先讀取到主庫的 GTID(即 set gtid_next 記錄),而後執行的事務採用該 GTID。
##### GTID 的好處
1. GTID 使用 `master_auto_position=1` 代替了 Binlog 的主從複製方案,相比 Binlog 方式更容易搭建主從複製。
2. GTID 方便實現主從之間的 failover(主從切換),不用一步一步的去定位 Binlog日誌檔案和查詢 Binlog 的位點資訊。
##### GTID 模式複製侷限性
1. 在一個事務裡面混合使用引擎,如 Innodb(支援事務)、MyISAM(不支援事務), 造成多個 GTIDs 和同一個事務相關聯出錯。
2. `CREATE TABLE…..SELECT` 不能使用,該語句產生的兩個 Event。 在某一情況會使用同一個 GTID(同一個 GTID 在 slave 只能被使用一次):
- event one:建立表語句 create table
- event two :插入資料語句 insert
3. `CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE` 不能在事務內使用 (啟用了 `–enforce-gtid-consistency` 引數)。
4. 使用 GTID 複製從庫跳過錯誤時,不支援 `sql_slave_skip_counter` 引數的語法。
##### GTID 主從複製實戰
1.Master 主資料庫上的操作
在 my.cnf 檔案中配置 GTID 主從複製
```mysql
[root@mysql-master ~]# cp /etc/my.cnf /etc/my.cnf.bak
[root@mysql-master ~]# >/etc/my.cnf
[root@mysql-master ~]# cat /etc/my.cnf
[mysqld]
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
symbolic-links = 0
log-error = /var/log/mysqld.log
pid-file = /var/run/mysqld/mysqld.pid
#GTID:
server_id = 1
gtid_mode = on
enforce_gtid_consistency = on
#binlog
log_bin = mysql-bin
log-slave-updates = 1
binlog_format = row
sync-master-info = 1
sync_binlog = 1
#relay log
skip_slave_start = 1
```
配置後,重啟 MySQL 服務:
```mysql
[root@mysql-master ~]# systemctl restart mysqld
```
登入 MySQL,並檢視 Master 狀態, 發現多了一項 `Executed_Gtid_Set`:
```mysql
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 154 | | | |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
mysql> show global variables like '%uuid%';
+---------------+--------------------------------------+
| Variable_name | Value |
+---------------+--------------------------------------+
| server_uuid | 317e2aad-1565-11e9-9c2e-005056ac6820 |
+---------------+--------------------------------------+
1 row in set (0.00 sec)
```
檢視確認 GTID 功能開啟:
```mysql
mysql> show global variables like '%gtid%';
+----------------------------------+-------+
| Variable_name | Value |
+----------------------------------+-------+
| binlog_gtid_simple_recovery | ON |
| enforce_gtid_consistency | ON |
| gtid_executed | |
| gtid_executed_compression_period | 1000 |
| gtid_mode | ON |
| gtid_owned | |
| gtid_purged | |
| session_track_gtids | OFF |
+----------------------------------+-------+
8 rows in set (0.00 sec)
```
檢視確認 Binlog 日誌功能開啟:
```mysql
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
1 row in set (0.00 sec)
```
授權 slave 複製使用者,並重新整理許可權:
```mysql
mysql> flush privileges;
Query OK, 0 rows affected (0.04 sec)
mysql> show grants for slave@'172.23.3.66';
+-------------------------------------------------------------------------------+
| Grants for [email protected] |
+-------------------------------------------------------------------------------+
| GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'172.23.3.66' |
+-------------------------------------------------------------------------------+
1 row in set (0.00 sec)
```
再次檢視 master 狀態:
```mysql
mysql> show master status;
+-------------------+----------+--------------+------------------+------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+------------------------------------------+
| mysql-bin.000001 | 622 | | | 317e2aad-1565-11e9-9c2e-005056ac6820:1-2 |
+-------------------+----------+--------------+------------------+------------------------------------------+
1 row in set (0.00 sec)
```
這裡需要注意一下:
啟動配置之前,同樣需要對從伺服器進行初始化。對從伺服器初始化的方法基本和基於日誌點是相同的,只不過在啟動了 GTID 模式後,在備份中所記錄的就不是備份時的二進位制日誌檔名和偏移量了,而是記錄的是備份時最後的 GTID 值。
需要先在主資料庫機器上把目標庫備份一下,假設這裡目標庫是 slave_test:
```mysql
mysql> CREATE DATABASE slave_test CHARACTER SET utf8 COLLATE utf8_general_ci;
Query OK, 1 row affected (0.02 sec)
mysql> use slave_test;
Database changed
mysql> create table user (id int(10) PRIMARY KEY AUTO_INCREMENT,name varchar(50) NOT NULL);
Query OK, 0 rows affected (0.27 sec)
mysql> insert into slave_test.user values(1,"xiaoming"),(2,"xiaohong"),(3,"xiaolv");
Query OK, 3 rows affected (0.06 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from slave_test.user;
+----+----------+
| id | name |
+----+----------+
| 1 | xiaoming |
| 2 | xiaohong |
| 3 | xiaolv |
+----+----------+
3 rows in set (0.00 sec)
```
把 slave_test 庫備份出來:
```mysql
[root@mysql-master ~]# mysqldump --single-transaction --master-data=2 --triggers --routines --databases slave_test -uroot -p123456 > /root/user.sql
```
這裡有個版本上的問題:
MySQL 5.6 使用 `mysqldump` 備份時,指定備份的具體庫,使用 `--database`。
MySQL 5.7 使用 `mysqldump` 備份時,指定備份的具體庫,使用`--databases。`
然後把備份的` /root/user.sql` 檔案拷貝到 slave 從資料庫伺服器上。
```mysql
[root@mysql-master ~]# rsync -e "ssh -p20" -avpgolr /root/user.sql
```
到這裡主庫的操作結束,包含 GTID 的備份資料已經 copy 到從庫,下面來進行從庫的操作。
2.從庫操作
在 my.cnf 檔案中配置 GTID 主從複製
與主伺服器配置大概一致,除了 server_id 不一致外,從伺服器還可以在配置檔案裡面新增`read_only=on` ,使從伺服器只能進行讀取操作,此引數對超級使用者無效,並且不會影響從伺服器的複製。
```mysql
[root@mysql-slave1 ~]# >/etc/my.cnf
[root@mysql-slave1 ~]# vim /etc/my.cnf
[mysqld]
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
symbolic-links = 0
log-error = /var/log/mysqld.log
pid-file = /var/run/mysqld/mysqld.pid
#GTID:
server_id = 2
gtid_mode = on
enforce_gtid_consistency = on
#binlog
log_bin = mysql-bin
log-slave-updates = 1
binlog_format = row
sync-master-info = 1
sync_binlog = 1
#relay log
skip_slave_start = 1
read_only = on
```
配置完成後,重啟mysql服務。
```mysql
[root@mysql-slave1 ~]# systemctl restart mysql
```
接著將主資料庫目標庫的備份資料 `user.sql`匯入到從資料庫裡。
```mysql
[root@mysql-slave1 ~]# ls /root/user.sql
/root/user.sql
[root@mysql-slave1 ~]# mysql -p123456
.........
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
mysql> source /root/user.sql;
mysql> select * from slave.test;
+----+----------+
| id | name |
+----+----------+
| 1 | xiaoming |
| 2 | xiaohong |
| 3 | xiaolv |
+----+----------+
3 rows in set (0.00 sec)
```
在從資料庫裡,使用 `change master` 配置主從複製:
```mysql
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> change master to master_host='172.23.3.66',master_user='slave1',master_password='123456',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.26 sec)
mysql> start slave;
Query OK, 0 rows affected (0.02 sec)
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.23.3.66
Master_User: slave1
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 1357
Relay_Log_File: mysql-slave1-relay-bin.000002
Relay_Log_Pos: 417
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
................
................
Executed_Gtid_Set: 317e2aad-1565-11e9-9c2e-005056ac6820:1-5
Auto_Position: 1
```
由此,Master 和 Slave 節點已經配置了主從同步關係。接下來你可以自行在主庫插入一條資料觀察從庫是否同步過來。
##### 使用 GTID 新增從庫有兩種方式
**直接同步主庫所有GTID**
如果主庫一開始就開啟了 GTID,那麼可以直接獲取主庫的所有GTID來同步至從庫。但是如果主庫 Binlog 日誌太多,那麼相應同步的時間也會變長。這種方式適用於小資料量的同步。
使用這種方式來同步相應的命令為:
```mysql
mysql>change master to master_host='xxxxxxx',master_user='xxxxxx',master_password='xxxxx',MASTER_AUTO_POSITION=1;
mysql> start slave;
mysql> stop slave io_thread; #重啟 io 執行緒,重新整理狀態
mysql> start slave io_thread;
```
**當使用 `MASTER_AUTO_POSITION` 引數的時候,`MASTER_LOG_FILE`,`MASTER_LOG_POS` 引數不能使用。**
**如果想要從 `GTID 配置回 pos`,再次執行這條語句,不過把 MASTER_AUTO_POSITION 置為 0。**
**通過設定範圍進行同步**
通過指定 GTID 的範圍,然後通過在 slave 設定 `@@GLOBAL.GTID_PURGED` 從而跳過備份包含的 GTID 。
這種方案適用於資料量比較大一次同步需要耗費巨量時間的資料。但同時也有操作複雜的問題,需要你記住每次同步的範圍。
用這種方式來同步相應的命令為:
```mysql
mysql>change master to master_host='xxxxxxx',master_user='xxxxxx',master_password='xxxxx',MASTER_LOG_POS='xxxx';
mysql> start slave;
mysql> stop slave io_thread; #重啟 io 執行緒,重新整理狀態
mysql> start slave io_thread;
```
這裡注意到我們的引數換了:`MASTER_LOG_POS`,該引數表示當前需要同步的 GTID 事務的起點值。
#### 總結
本篇介紹主從複製的兩種形式:基於 Binlog 和 位點資訊的傳統複製方式;基於 Binlog 和 GTID 的新式複製方式。現在很多公司可能還在使用 MySQL 5.6 的版本,所以 GTID不一定可以使用。
本文篇幅較長,大家可以簡要檢視