[toc]
## 使用主從的好處
1. 采用主從服務器這種架構,穩定性得以提升。如果主服務器發生故障,我們可以使用從服務器來提供服務。
2. 在主從服務器上分開處理用戶的請求,如讀寫分離,可以提升數據處理效率。
3. 將主服務器上的數據復制到從服務器上,保護數據免受意外的損失(數據熱備)。
## 原理(基于Binlog)
主從復制有兩種方式
* 基于Binlog
* 基于GTID(全局事務標識符)

1. 主庫記錄二進制日志,每次準備提交事務完成數據庫更新前,先記錄二進制日志,記錄二進制日志后,主庫會告訴存儲引擎可以提交事務了
2. 備庫將主庫的二進制日志復制到本地的中繼日志中,首先,備庫會先啟動一個工作進程,稱為IO工作線程,負責和主庫建立一個普通的客戶端連接。如果該進程追趕上了主庫,它將進入睡眠狀態,直到主庫有新的事件產生通知它,他才會被喚醒,將接收到的事件記錄到中繼日志中。
3. 備庫的SQL線程執行最后一步,該線程從中繼日志中讀取事件并且在備庫執行,當SQL線程趕上IO線程的時候,中繼日志通常記錄在系統緩存中,所以中繼日志的開銷很低。SQL線程也可以根據配置選項來決定是否寫入其自己的二進制日志中。
## 復制的方式
**異步復制**
MySQL復制默認是異步復制,Master將事件寫入binlog,提交事務,自身并不知道slave是否接收是否處理;
缺點:不能保證所有事務都被所有slave接收。
**同步復制**
Master提交事務,直到事務在所有slave都已提交,才會返回客戶端事務執行完畢信息;
缺點:完成一個事務可能造成延遲。
**半同步復制**
當Master上開啟半同步復制功能時,至少有一個slave開啟其功能。當Master向slave提交事務,且事務已寫入relay-log中并刷新到磁盤上,slave才會告知Master已收到;若Master提交事務受到阻塞,出現等待超時,在一定時間內Master 沒被告知已收到,此時Master自動轉換為異步復制機制;
注:半同步復制功能要在Master和slave上開啟才會起作用,只開啟一邊,依然是異步復制。
## 關于主從復制和讀寫分離
主從復制只是實現讀寫分離的基礎,要實現讀寫分離還需要借助數據庫中間件或者程序實現。
## 主從復制注意點
1. 主從服務器操作**系統版本和位數一致**
2. Master 和 Slave 數據庫的**版本要一致**
3. 同步之前Master 和 Slave 數據庫中的**數據要一致**
4. Master 開啟二進制日志,Master 和 Slave 的 **server\_id 在局域網內必須唯一**
## 配置
master : 172.30.0.2
slave: 172.30.0.3
### master
**在 \[mysqld\] 中增加以下配置項**
~~~
## 設置 server_id,一般設置為局域網IP最后一段
server_id=2
## 復制過濾:需要備份的數據庫,輸出 binlog,如果有多項就復制多段
# 如果有過濾,不建議在master端處理。Slave端操作粒度更細,并且master全量復制不需要另外修改。
#binlog-do-db=test
#binlog-do-db=test2
## 復制過濾:不需要備份的數據庫,不輸出(mysql 庫一般不同步)
binlog-ignore-db=mysql
## 開啟二進制日志功能,可以隨便取,最好有含義
log-bin=edu-mysql-bin
## 為每個 session 分配的內存,在事務過程中用來存儲二進制日志的緩存
binlog_cache_size=1M
## 主從復制的格式(mixed,statement,row,默認格式是 statement)
binlog_format=mixed
## 二進制日志自動刪除/過期的天數。默認值為 0,表示不自動刪除。
expire_logs_days=7
## 跳過主從復制中遇到的所有錯誤或指定類型的錯誤,避免 slave 端復制中斷。
## 如:1062 錯誤是指一些主鍵重復,1032 錯誤是因為主從數據庫數據不一致
slave_skip_errors=1062
~~~
**關于MySQL 對于二進制日志 (binlog)的復制類型**
1. 基于語句的復制:在 Master 上執行的 SQL 語句,在 Slave 上執行同樣的語句。MySQL 默認采用基于語句的復制,效率比較高。一旦發現沒法精確復制時,會自動選著基于行的復制。
2. 基于行的復制:把改變的內容復制到 Slave,而不是把命令在 Slave 上執行一遍。從MySQL5.0 開始支持。
3. 混合類型的復制:默認采用基于語句的復制,一旦發現基于語句的無法精確的復制時,就會采用基于行的復制。
**創建數據同步用戶,并授予相應的權限**
~~~
mysql -uroot -p
# 5.7以前(會有個warnning)
mysql> grant replication slave, replication client on *.* to 'repl'@'172.30.0.3' identified by 'caiwen.123'
# 5.7以后
create user 'dba'@'172.30.0.%' identified by 'caiwen.123';
grant replication slave on *.* to dba@'172.30.0.%';
# 刷新授權表信息
mysql> flush privileges;
# 查看 position 號,記下 position 號(從機上需要用到這個 position 號和現在的日志文件)
mysql> show master status;
~~~

<br />
**鎖表同步數據**
~~~
# 鎖定當前表
mysql> flush tables with read lock;
# 進行備份同步當前數據到主庫
# 解鎖表
mysql> unlock tables;
~~~
### slave
**在 \[mysqld\] 中增加以上的master配置項,然后添加或修改以下選項**
~~~
## 設置 server_id,一般設置為 IP
server_id=3
## 開啟二進制日志,以備 Slave 作為其它 Slave 的 Master 時使用
log-bin=edu-mysql-slave1-bin
## relay_log 配置中繼日志
relay_log=edu-mysql-relay-bin
## log_slave_updates 表示 slave 將復制事件寫進自己的二進制日志
log_slave_updates=1
## 防止改變數據(除了特殊的線程)
read_only=1
~~~
如果 Slave 為其它 Slave 的 Master 時,必須設置 bin\_log。在這里,我們開啟了二進制日志,而且顯式的命名(默認名稱為 hostname,但是,如果 hostname 改變則會出現問題)。
relay\_log 配置中繼日志,log\_slave\_updates 表示 slave 將復制事件寫進自己的二進制日志。當設置 log\_slave\_updates 時,你可以讓 slave 扮演其它 slave 的 master。此時,slave 把 SQL線程執行的事件寫進行自己的二進制日志(binary log),然后,它的 slave 可以獲取這些事件并執行它。如下圖所示(發送復制事件到其它 Slave):

~~~
mysql -uroot -p
# 配置主從
# master_log_file ##指定 Slave 從哪個日志文件開始讀復制數據
#(可在 Master 上使用 show master status 查看到日志文件名)
# master_log_pos=429 ## 從哪個 POSITION 號開始讀
# master_connect_retry=30 ##當重新建立主從連接時,如果連接建立失敗,間隔多久后重試。
# 單位為秒,默認設置為 60 秒,同步延遲調優參數。
mysql> change master to master_host='172.30.0.2',master_user='repl', master_password='caiwen.123', master_port=3306, master_log_file='edu-mysql-bin.000001', master_log_pos=618, master_connect_retry=30;
## 查看主從同步狀態
mysql> show slave status\G;
# 可看到 Slave_IO_State 為空, Slave_IO_Running 和 Slave_SQL_Running 是 No,表明 Slave 還 沒有開始復制過程。
## 開啟主從同步
mysql> start slave;
## 再查看主從同步狀態
mysql> show slave status\G;
~~~
### 查看狀態
可查看 master 和 slave 上線程的狀態。在 master 上,可以看到 slave 的 I/O 線程創建的連接:
~~~
Master : mysql> show processlist\G;
~~~

~~~
Slave: mysql> show processlist\G;
~~~

2\. row : 為 I/O 線程狀態
3\. row :為 SQL 線程狀態。
<br />
當主從復制正在進行中時,如果想查看從庫兩個線程運行狀態,可以通過執行在從庫里執行”show slave statusG”語句,以下的字段可以給你想要的信息:
~~~
Master_Log_File — 上一個從主庫拷貝過來的binlog文件
Read_Master_Log_Pos — 主庫的binlog文件被拷貝到從庫的relay log中的位置
Relay_Master_Log_File — SQL線程當前處理中的relay log文件
Exec_Master_Log_Pos — 當前binlog文件正在被執行的語句的位置
~~~
### 重置主從復制設置
測試過程中,如果遇到同步出錯,可在 Slave 上重置主從復制設置(選操作):
```
(1) mysql> reset slave;
(2) mysql> change master to master_host='172.30.0.2',master_user='repl', master_password='caiwen.123', master_port=3306, master_log_file='edu-mysql-bin.000001', master_log_pos=618, master_connect_retry=30;
(此時,master_log_file 和 master_log_pos 要在 Master 中用 show master status 命令查看)
```
>[warning] 注意:如果在 Slave 沒做只讀控制的情況下,千萬不要在 Slave 中手動插入數據,那樣數據 就會不一致,主從就會斷開,就需要重新配置了。
### 主從相關命令
~~~
show master status; //查看master的狀態,尤其是當前的日志及位置
show slave status; //查看slave的狀態
reset slave; //重置slave狀態
start slave; //啟動slave狀態(開啟監聽master的變化)
stop slave; //暫停salve狀態
~~~
## 關于主主復制
兩臺服務器上都開啟二進制日志和relay日志,都設置replication賬號,都設置對方為自己的master。 即可完成主主復制。
但是,這樣配置主主復制會出現很多同步沖突的問題,一般都是通過第三方工具來處理。后續會介紹。
## 關于半同步復制
我們知道,普通的replication,即MySQL的異步復制,依靠MySQL二進制日志也即binary log進行數據復制。比如兩臺機器,一臺主機(master),另外一臺是從機(slave)。
1)正常的復制為:事務一(t1)寫入binlog buffer;dumper線程通知slave有新的事務t1;binlog buffer進行checkpoint;slave的io線程接收到t1并寫入到自己的的relay log;slave的sql線程寫入到本地數據庫。 這時,master和slave都能看到這條新的事務,即使master掛了,slave可以提升為新的master。
2)異常的復制為:事務一(t1)寫入binlog buffer;dumper線程通知slave有新的事務t1;binlog buffer進行checkpoint;slave因為網絡不穩定,一直沒有收到t1;master掛掉,slave提升為新的master,t1丟失。
3)很大的問題是:主機和從機事務更新的不同步,就算是沒有網絡或者其他系統的異常,當業務并發上來時,slave因為要順序執行master批量事務,導致很大的延遲。
為了彌補以上幾種場景的不足,MySQL從5.5開始推出了半同步復制。相比異步復制,半同步復制提高了數據完整性,因為很明確知道,在一個事務提交成功之后,這個事務就至少會存在于兩個地方。即在master的dumper線程通知slave后,增加了一個ack(消息確認),即是否成功收到t1的標志碼,也就是dumper線程除了發送t1到slave,還承擔了接收slave的ack工作。如果出現異常,沒有收到ack,那么將自動降級為普通的復制,直到異常修復后又會自動變為半同步復制。
半同步復制具體特性:
* 從庫會在連接到主庫時告訴主庫,它是不是配置了半同步。
* 如果半同步復制在主庫端是開啟了的,并且至少有一個半同步復制的從庫節點,那么此時主庫的事務線程在提交時會被阻塞并等待,結果有兩種可能,要么至少一個從庫節點通知它已經收到了所有這個事務的Binlog事件,要么一直等待直到超過配置的某一個時間點為止,而此時,半同步復制將自動關閉,轉換為異步復制。
* 從庫節點只有在接收到某一個事務的所有Binlog,將其寫入并Flush到Relay Log文件之后,才會通知對應主庫上面的等待線程。
* 如果在等待過程中,等待時間已經超過了配置的超時時間,沒有任何一個從節點通知當前事務,那么此時主庫會自動轉換為異步復制,當至少一個半同步從節點趕上來時,主庫便會自動轉換為半同步方式的復制。
* 半同步復制必須是在主庫和從庫兩端都開啟時才行,如果在主庫上沒打開,或者在主庫上開啟了而在從庫上沒有開啟,主庫都會使用異步方式復制。

## MySQL 主從數據同步延遲問題的調優
基于局域網的 Master/Slave 機制在通常情況下已經可以滿足“實時”備份的要求了。如果延
遲比較大,可以從以下幾個因素進行排查:
(1) 網絡延遲;
(2) Master 負載過高;
(3) Slave 負載過高;
一般的做法是使用多臺Slave來分攤讀請求,再單獨配置一臺Slave只作為備份用,不進行 其他任何操作,就能相對最大限度地達到“實時”的要求了。MySQL 5.7之后,可以使用多線程復制,使用MGR復制架構
- 【mysql的編程專題①】流程控制與其他語法
- 【mysql的編程專題②】觸發器
- 【mysql的編程專題③】內置函數
- 【mysql的編程專題④】存儲過程
- 【mysql的編程專題⑤】自定義函數
- 【mysql的編程專題⑥】視圖
- 【mysql的設計與優化專題(1)】ER圖,數據建模與數據字典
- 【mysql的設計與優化專題(2)】數據中設計中的范式與反范式
- 【mysql的設計與優化專題(3)】字段類型與合理的選擇字段類型
- 【mysql的設計與優化專題(4)】表的垂直拆分和水平拆分
- 【mysql的設計與優化專題(5)】慢查詢詳解
- 【mysql的設計與優化專題(6)】mysql索引攻略
- 【Mysql問題集錦(1)】mysql不能使用innodb存儲引擎
- 【Mysql進階技巧(2)】利用mysql生成唯一序號
- 【Mysql進階技巧(1)】MySQL的多表關聯與自連接
- 【Mysql高可用架構(1)】基于日志點的主從復制
- 【Mysql高可用架構(2)】主從管理的系統視圖
- 【Mysql高可用架構(3)】基于GTID的主從復制
- 【Mysql高可用架構(4)】在線變更復制類型
- 【Mysql高可用架構(5)】多源復制(多主一從)
- 【Mysql高可用架構(6)】多線程復制
- 【Mysql高可用架構(7)】在線設置復制過濾
- 【Mysql高可用架構(8)】解決主從不一致
- 【Mysql高可用架構(9)】初識mycat以及制作mycat鏡像
- 【Mysql高可用架構(10)】mycat配置mysql讀寫分離
- MyCat 集群部署(HAProxy + MyCat)
- 常用復雜sql語句整理