MySQL 的複製功能不僅可以構建高性能的應用,也是高可用性、可擴展性、災難恢復等工作的基礎。
複製的主要功能
1、數據分佈。
MySQL 提供的複製功能並不需要很大的帶寬的要求,因此可以在不同的物理位置來分佈數據。
2、讀操作的負載均衡
通過複製與負載均衡方案,可以將讀操作分佈到多個從服務器上,實現對讀密集型應用的性能優化。
3、備份
複製能起到一定的備份作用,但是並不能完全取代備份。
4、高可用性和故障轉移
複製能夠幫助避免 MySQL 單點失敗的問題,當主庫宕機時,迅速的故障切換能減少系統的停機時間。
基於日誌點複製的原理
大體來說,複製分爲以下幾步:
1、主庫將數據的更新事件記錄到二進制日誌。
二進制日誌由各種事件組成,一個事件通常爲一個更新加一些其它信息,比如時間戳。在每次準備提交事務前,主庫將數據更新的事件記錄到二進制日誌中。記錄完畢後,主庫將告訴存儲引擎可以提交事件了。
2、從庫將主庫上的日誌複製到自己的中繼日誌中。
首先,從庫啓動一個工作線程,稱爲 I/O 線程,該線程跟主庫建立一個普通的連接,然後在主庫上啓動一個二進制轉儲(binlog dump)線程,這個二進制存儲線程會讀取並向從庫發送主庫上二進制日誌中的事件,從庫的 I/O 線程接收到事件並將其記錄到從庫的中繼日誌中。
注意,二進制存儲線程的工作方式不是輪詢,當該線程追趕上了主庫,它就進入休眠狀態,直到主庫發送信號通知來將其喚醒。
3、從庫讀取中繼日誌中的事件,將其重放到從庫中。
當主庫的時間發送過來時,從庫的 I/O線程將事件寫入中繼日誌(relay log)中,然後從庫上 SQL 線程就去讀取並且重放中繼日誌中的事件,達到與主庫數據的一致。可以看到, I/O 線程和 SQL 線程的協作,使得獲取事件和重放事件的解耦,兩者異步執行。在 5.6之前,只能有一個 SQL 線程工作,因此重放是串行化進行的。在5.6之後,增加了多線程重放,但是注意,多線程是基於庫的,一個庫只能使用一個 SQL 線程重放事件。
複製的步驟
1、創建複製賬號
從庫中運行的 I/O 線程會建立一個到主庫的連接,因此需要在主庫上創建個用戶,並且擁有適當的權限,從庫將以該用戶的身份連接到主庫上讀取其二進制日誌。
建立一個只有使用使用內網 IP 才能登陸的賬號:
mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO repl@’192.168.xxx.%’ IDENTIFIED BY ‘password’;
該命令創建 repl賬號,同時爲授予 REPLICATION SLAVE 與 REPLICATION CLIENT 權限。注意,實際上只需要 REPLICATION SLAVE 權限就可以進行復制了。而 REPLICATION CLIENT 權限主要是允許該賬號對複製進行監控和管理,比如 SHOW MASTER STATUS、SHOW SLAVE STATUS、SHOW BINARY LOGS 語句的使用。
賬戶建立好後,應該在從庫主機上嘗試着使用該賬號遠程登錄主庫,如果順利登陸,說明兩臺主機的 MySQL 能正常通信,並且賬號沒問題。
2、對主、從庫進行配置
對主、從庫進行必要的配置:
主:
# 該命令啓動二進制日誌,並且指定二進制日誌位置與名稱
log_bin = mysql-bin
# 這裏必須爲主庫設置一個唯一的 ID
server_id = 1
備:
log_bin = mysql-bin
# 確保唯一性
server_id = 2
# 指定中繼日誌的位置與名稱
relay_log = /var/lib/mysql/mysql-relay-bin
# 命令從庫將其重放的事件記錄到自己的二進制日誌中
log_slave_updates = 1
# 阻止沒有特權權限的線程修改數據
read_only = 1
實際上,只有 server_id 是必須設置的,其他的配置項根據實際情況進行配置。例如,如果從庫也是其他庫的主庫,那麼就要將 log_slave_updates 設置爲1。如果從庫只用來讀數據,則要將 read_only 設置爲1。
3、初始化數據
使用 mysqldump 或者其他方法對主庫數據進行全量備份,然後在從庫上恢復該備份。注意,主庫備份的時候,要記錄當前的二進制日誌的名字及座標(即偏移量)。可以通過 SHOW MASTER STATUS 命令來查看。
4、開始複製
數據初始化好後,就可以開始複製了。
mysql-> CHANGE MASTER TO MASTER_HOST = ‘主機IP’,
-> MASTER_USER = ‘repl’,
-> MASTER_PASSWORD = ‘password’,
-> MASTER_LOG_FILE = ‘主庫上當前二進制文件’,
-> MASTER_LOG_POS = ‘日誌座標’;
開始複製後,可以隨時使用 CHANGE MASTER TO 來動態地更改複製的設置,而不需要重啓從庫。
注意,一定要獲得正確的二進制日誌座標,I/O線程會去從日誌座標開始一直讀到日誌最後,然後進入休眠狀態,直到主庫發送信號喚醒。
設置好後,使用
mysql-> START SLAVE;
可以使用 SHOW SLAVE STATUS 命令來查看情況,如果 SLAVE_IO_STATUS 是 Waiting for master to send event,那麼就說明覆制已經正常開始了。
查看從庫狀態
通過在從庫上執行命令
mysql-> show slave status;
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.136.1
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000010
Read_Master_Log_Pos: 3266
Relay_Log_File: mysql-relay-bin.000002
Relay_Log_Pos: 877
Relay_Master_Log_File: mysql.000010
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
……
Exec_Master_Log_Pos: 3266
Relay_Log_Space: 1084
……
Seconds_Behind_Master: 0
……
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
主要變量解釋:
Slave_IO_State 爲 I/O線程的狀態,Waiting for master to send event 說明該線程正在等待主庫發送事件。
Read_Master_Log_Pos 爲 I/O線程讀取主庫上的二進制日誌到了哪個位置,單位爲字節。與主庫上二進制日誌記錄座標進行對比,可以得到當前 I/O 線程的延時。
Exec_Master_Log_Pos 爲 SQL線程重複主庫上的二進制日誌到了哪個位置,單位爲字節,(Read_Master_Log_Pos - Exec_Master_Log_Pos )可以表示當前 SQL 線程運行的延時。 Read_Master_Log_Pos 總是大於等於 Exec_Master_Log_Pos 。
Seconds_Behind_Master 是通過比較 SQL線程執行的最後一個事件裏記錄的時間戳和 I/O線程獲取到的最後一個事件裏記錄的時間戳進行比較,而得到的這麼一個差值。該值並不能完全反應主從延遲,考慮這種情況:當主庫I/O負載很大或是網絡阻塞, I/O線程不能及時獲取到二進制日誌裏的事件,而 SQL線程一直都能跟上 I/O線程的腳步,這時Seconds_Behind_Master 的值爲0,但此時主從之間是有延遲的。
SQL_Delay 5.6之後新增的延遲複製,表示獲取到主庫的事件之後, SQL線程還要等待N秒才重放。
SQL_Remaining_Delay 如果 SQL線程在等待過程中,則表示剩餘等待時間,其他時候爲0。
Slave_SQL_Running_State SQL線程的狀態
基於 GTID 複製
基於日誌點的複製需要對從庫指定二進制日誌的偏移量,如果偏移量有差錯,會導致同步後數據不一致的問題,並且可能會報錯而終止複製。爲了解決這個問題,MySQL 在5.6之後推出了基於 GTID 的複製。
GTID 簡單地說就是 MySQL 爲事務生成的 ID,它是全局唯一的,即在整個集羣中都是唯一的。
從庫會告訴主庫已執行的事務的 GTID 值,主庫會告訴從庫哪些GTID 事務沒有被執行。這一切都是由 MySQL 維護進行的,因此能更好地保證主從數據的一致性。