環境
- 主節點 Master
IP: 192.168.175.200
操作系統: CentOS-6.7-x86_64
MySQL 版本: mysql-5.6.26
主機名:huachao - 從節點 Slave
IP: 192.168.175.201
操作系統: CentOS-6.7-x86_64
MySQL 版本: mysql-5.6.26
主機名:mini1
介紹
MySQL 主從復制官方文檔
http://dev.mysql.com/doc/refman/5.6/en/replication.html
MySQL 主從復制的方式
MySQL5.6 開始主從復制有兩種方式:基于日志( binlog)、基于 GTID(全局事務標示符)。
本教程主要講基于日志( binlog) 的復制。-
MySQL 主從復制(也稱 A/B 復制) 的原理
(1) Master將數據改變記錄到二進制日志(binary log)中,也就是配置文件 log-bin指定的文件,
這些記錄叫做二進制日志事件(binary log events);
(2) Slave 通過 I/O 線程讀取 Master 中的 binary log events 并寫入到它的中繼日志(relay log);
(3) Slave 重做中繼日志中的事件, 把中繼日志中的事件信息一條一條的在本地執行一次,完
成數據在本地的存儲, 從而實現將改變反映到它自己的數據(數據重放)。
MySQL主從復制原理圖 主從配置需要注意的點
(1)主從服務器操作系統版本和位數一致;
(2) Master 和 Slave 數據庫的版本要一致;
(3) Master 和 Slave 數據庫中的數據要一致;
(4) Master 開啟二進制日志, Master 和 Slave 的 server_id 在局域網內必須唯一;
主從配置的簡要步驟
1、 Master 上的配置
(1) 安裝數據庫;
(2) 修改數據庫配置文件, 指明 server_id, 開啟二進制日志(log-bin);
(3) 啟動數據庫, 查看當前是哪個日志, position 號是多少;
(4) 登錄數據庫, 授權數據復制用戶( IP 地址為從機 IP 地址, 如果是雙向主從, 這里的
還需要授權本機的 IP 地址, 此時自己的 IP 地址就是從 IP 地址);
(5) 備份數據庫( 記得加鎖和解鎖);
(6) 傳送備份數據到 Slave 上;
(7) 啟動數據庫;
以下步驟, 為單向主從搭建成功, 想搭建雙向主從需要的步驟:
(1) 登錄數據庫, 指定 Master 的地址、 用戶、 密碼等信息( 此步僅雙向主從時需要);
(2) 開啟同步, 查看狀態;
2、 Slave 上的配置
(1) 安裝數據庫;
(2) 修改數據庫配置文件, 指明 server_id( 如果是搭建雙向主從的話, 也要開啟二進制日志 log-bin);
(3) 啟動數據庫, 還原備份;
(4) 查看當前是哪個日志, position 號是多少( 單向主從此步不需要, 雙向主從需要);
(5) 指定 Master 的地址、 用戶、 密碼等信息;
(6) 開啟同步, 查看狀態。
單向主從環境(也稱 MySQL A/B 復制)的搭建
Master( 192.168.175.200) 和 Slave( 192.168.175.201) 上都安裝了相同版本的數據庫( mysql-
5.6.26.tar.gz),參考 《 高可用架構篇--第 13 節--MySQL 源碼編譯安裝( CentOS6.6+MySQL5.6)》。
注意:兩臺數據庫服務器的的 selinux 都要 disable ( 永久關閉 selinux,請修改/etc/selinux/config,
將 SELINUX 改為 disabled)修改 Master 的配置文件/etc/my.cnf
[root@huachao ~]# vi /etc/my.cnf
## 在 [mysqld] 中增加以下配置項
## 設置 server_id,一般設置為 IP
server_id=205
## 復制過濾: 需要備份的數據庫, 輸出 binlog
#binlog-do-db=roncoo
## 復制過濾: 不需要備份的數據庫, 不輸出( 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
(如想了解以上參數的更多詳細解析, 大家可以直接百度參數名)
-
復制過濾可以讓你只復制服務器中的一部分數據,有兩種復制過濾:
(1) 在 Master 上過濾二進制日志中的事件;
(2) 在 Slave 上過濾中繼日志中的事件。如下:
MySQL主從復制原理圖--復制過濾 - MySQL 對于二進制日志 (binlog)的復制類型
(1) 基于語句的復制:在 Master 上執行的 SQL 語句,在 Slave 上執行同樣的語句。 MySQL 默認采用基于語句的復制,效率比較高。一旦發現沒法精確復制時,會自動選著基于行的復制。
(2) 基于行的復制:把改變的內容復制到 Slave,而不是把命令在 Slave 上執行一遍。從MySQL5.0 開始支持。
(3) 混合類型的復制:默認采用基于語句的復制,一旦發現基于語句的無法精確的復制時,就會采用基于行的復制。
主機配置
vi /etc/my.cnf
#主從復制配置
## replication
server_id=200
#binlog-do-db=huachao
binlog-ignore-db=mysql
log-bin=edu-mysql-bin
binlog_cache_size=1M
binlog_format=mixed
expire_logs_days=7
slave_skip_errors=1062
- 啟動/重啟 Master 數據庫服務,登錄數據庫, 創建數據同步用戶,并授予相應的權限
[root@huachao ~]# service mysql restart
Shutting down MySQL..[ OK ]
Starting MySQL..[ OK ]
[root@huachao ~]# mysql -uroot -p
創建數據同步用戶,并授予相應的權限
grant replication slave, replication client on *.* to 'repl'@'192.168.175.201' identified by 'huachao.123';
刷新授權表信息
flush privileges;
查看 position 號, 記下 position 號( 從機上需要用到這個 position 號和現在的日志文件)
show master status;
- 創建 huachao庫、 表, 并寫入一定量的數據, 用于模擬現有的業務系統數據庫
create database if not exists huachao default charset utf8 collate utf8_general_ci;
use huachao;
DROP TABLE IF EXISTS `edu_user`;
CREATE TABLE `edu_user` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(255) NOT NULL DEFAULT '' COMMENT '用戶名',
`pwd` varchar(255) NOT NULL DEFAULT '' COMMENT '密碼',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用戶信息表';
INSERT INTO `edu_user` VALUES (1,'吳水成','123456'),(2,'清風','123456'),(3,'龍果','roncoo.com');
- 為保證 Master 和 Slave 的數據一致,我們采用主備份,從還原來實現初始數據一致
先臨時鎖表
mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)
mysql> exit;
Bye
這里我們實行全庫備份,在實際中,我們可能只同步某一個庫,那也可以只備份一個庫
mysqldump -p3306 -uroot -p --add-drop-table huachao > /tmp/edu-master-huachao.sql;
進入tmp目錄
[root@huachao ~]# cd /tmp
[root@huachao tmp]# ll
注意: 實際生產環境中大數據量(超 2G 數據)的備份,建議不要使用 mysqldump 進行備份, 因為會非常慢。 此時推薦使用 XtraBackup 進行備份。
解鎖表
[root@huachao ~]# mysql -uroot -p
Enter password:
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql>
將 Master 上備份的數據遠程傳送到 Slave 上,以用于 Slave 配置時恢復數據
scp /tmp/edu-master-huachao.sql root@192.168.175.201:/tmp/
從機配置
接下來處理 Slave( 192.168.175.201), 配置文件只需修改一項, 其余配置用命令來操作
[root@mini1 ~]# vi /etc/my.cnf
## 在 [mysqld] 中增加以下配置項
## 設置 server_id,一般設置為 IP
server_id=206
## 復制過濾: 需要備份的數據庫,輸出 binlog
#binlog-do-db=roncoo
##復制過濾: 不需要備份的數據庫, 不輸出( mysql 庫一般不同步)
binlog-ignore-db=mysql
## 開啟二進制日志,以備 Slave 作為其它 Slave 的 Master 時使用
log-bin=edu-mysql-slave1-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
## 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):
vi /etc/my.cnf
## replication
server_id=206
#binlog-do-db=huachao
binlog-ignore-db=mysql
log-bin=edu-mysql-slave1-bin
binlog_cache_size = 1M
binlog_format=mixed
expire_logs_days=7
slave_skip_errors=1062
relay_log=edu-mysql-relay-bin
log_slave_updates=1
read_only=1
- 保存后重啟 MySQL 服務,還原備份數據
[root@mini1 ~]# service mysql restart
Shutting down MySQL..[ OK ]
Starting MySQL..[ OK ]
Slave 上創建相同庫:
create database if not exists huachao default charset utf8 collate utf8_general_ci;
use huachao;
導入數據
mysql -uroot -p huachao < /tmp/edu-master-huachao.sql
- 登錄 Slave 數據庫, 添加相關參數
( Master 的 IP、 端口、 同步用戶、 密碼、 position 號、 讀取哪個日志文件)
[root@mini1 ~]# mysql -uroot -p
Enter password:
重點注意:master_log_pos需要在保存前再在Master上使用show master status;
再查看一次,然后再設置、保存,不要使用Master設置時候查詢的值,例如本例中在Master設置時查詢的Position=429,現在查詢的是1402,所有我們使用1402。
mysql> change master to master_host='192.168.175.200', master_user='repl', master_password='huachao.123', master_port=3306, master_log_file='edu-mysql-bin.000001',master_log_pos=1402, master_connect_retry=30;
上面執行的命令的解釋:
master_host='192.168.175.200' ## Master 的 IP 地址
master_user='repl' ## 用于同步數據的用戶(在 Master 中授權的用戶)
master_password='huachao.123' ## 同步數據用戶的密碼
master_port=3306 ## Master 數據庫服務的端口
master_log_file='edu-mysql-bin.000001' ##指定 Slave 從哪個日志文件開始讀復制數據( 可在 Master 上使用 show master status 查看到日志文件名)
master_log_pos=1402 ## 從哪個 POSITION 號開始讀
master_connect_retry=30 ##當重新建立主從連接時,如果連接建立失敗,間隔多久后重試。單位為秒, 默認設置為 60 秒, 同步延遲調優參數。
- 查看主從同步狀態
mysql> show slave status\G;
可看到 Slave_IO_State 為空, Slave_IO_Running 和 Slave_SQL_Running 是 No, 表明 Slave 還沒有開始復制過程。
- 開啟主從同步
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
- 再查看主從同步狀態
mysql> show slave status\G;
主要看以下兩個參數, 這兩個參數如果是 Yes 就表示主從同步正常
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
由截圖中的主從同步狀態信息可以看出,我們配置的主從同步是正常的。
可查看 master 和 slave 上線程的狀態。在 master 上,可以看到 slave 的 I/O 線程創建的連接:
Master :
mysql> show processlist\G;
1.row
為處理 slave 的 I/O 線程的連接。2.row
為處理 MySQL 客戶端連接線程。3.row
為處理本地命令行的線程。
Slave :
mysql> show processlist\G;
1.row
為 I/O 線程狀態。2.row
為 SQL 線程狀態。3.row
為處理本地命令行的線程。
- 主從數據復制同步測試
(1) 在 Master 中的 roncoo 庫上變更數據的同步測試;
mysql> INSERT INTO `edu_user` VALUES (4,'同步測試 1','123456'),(5,'同步測試 2','123456');
Master 中添加完之后,登錄 Slave 中查看數據是否已同步。
(2) 在 Master 上新建一個 ron 庫
mysql> create database if not exists ron default charset utf8 collate utf8_general_ci;
在 Slave 中查看數據庫
mysql> show databases;
最終的測試結果是,在 Master 中的操作, 都成功同步到了 Slave。
- 測試過程中,如果遇到同步出錯,可在 Slave 上重置主從復制設置( 選操作):
(1)
mysql> reset slave;
(2)
mysql> change master to master_host='192.168.175.200',
master_user='repl',
master_password='roncoo.123',
master_port=3306,
master_log_file='edu-mysql-bin.00000x',
master_log_pos=xx,
master_connect_retry=30;
(此時, master_log_file 和 master_log_pos 要在 Master 中用 show master status 命令查看)
注意: 如果在 Slave 沒做只讀控制的情況下, 千萬不要在 Slave 中手動插入數據, 那樣數據就會不一致, 主從就會斷開, 就需要重新配置了。
- 上面所搭建的是單向復制的主從, 也是用的比較多的,而雙向主從其實就是 Master 和Slave 都開啟日志功能, 然后在 Master 執行授權用戶( 這里授權的是自己作為從服務器, 也就是這里的 IP 地址是 Master 的 IP 地址), 然后再在 Master 上進行 chang master 操作。
MySQL 主從數據同步延遲問題的調優
基于局域網的 Master/Slave 機制在通常情況下已經可以滿足“實時”備份的要求了。如果延遲比較大,可以從以下幾個因素進行排查:
(1) 網絡延遲;
(2) Master 負載過高;
(3) Slave 負載過高;
一般的做法是使用多臺 Slave 來分攤讀請求,再單獨配置一臺 Slave 只作為備份用, 不進行其他任何操作,就能相對最大限度地達到“實時” 的要求了。
兩個可以減少主從復制延遲的參數( 按需配置):
MySQL 可以指定 3 個參數, 用于復制線程重連主庫: --master-retry-count, --master-connectretry, --slave-net-timeout。其中 master-connect-retry 和 master-retry-count 需要在 Change Master 搭建主備復制時指定,而 slave-net-timeout 是一個全局變量,可以在 MySQL 運行時在線設置。具體的重試策略為:備庫過了 slave-net-timeout 秒還沒有收到主庫來的數據,它就會開始第一次重試。然后每過master-connect-retry
秒,備庫會再次嘗試重連主庫。直到重試了master-retry-count 次,它才會放棄重試。如果重試的過程中,連上了主庫,那么它認為當前主庫是好的,又會開始 slave-net-timeout 秒的等待。 slave-net-timeout 的默認值是 3600 秒, master-connect-retry 默認為 60 秒, master-retry-count 默認為 86400 次。也就是說,如果主庫一個小時都沒有任何數據變更發送過來,備庫才會嘗試重連主庫。這就是為什么在我們模擬的場景下,一個小時后,備庫才會重連主庫,繼續同步數據變更的原因。這樣的話,如果你的主庫上變更比較頻繁,可以考慮將 slave-net-timeout 設置的小一點,避免主庫 Binlog dump 線程終止了,無法將最新的更新推送過來。當然 slave-net-timeout 設置的過小也有問題,這樣會導致如果主庫的變更確實比較少的時候,備庫頻繁的重新連接主庫,造成資源浪費。
slave-net-timeout=seconds
參數說明: 當 Slave 從 Master 數據庫讀取 log 數據失敗后, 等待多久重新建立連接并獲取數據, 單位為秒, 默認設置為 3600 秒。
在做 MySQL Slave 的時候經常會遇到很多錯誤,需要根據具體原因跨過錯誤繼續同步,但有時候是因為網絡不穩定、網絡閃斷造成同步不正常,如果 Slave 機器非常多的情況下,一個一個登錄服務器去 stop slave、 start slave 變得無聊而且重復。從 MySQL5.1 開始支持的解決
方案配置:
master-connect-retry=seconds
參數說明: 在主服務器宕機或連接丟失的情況下,從服務器線程重新嘗試連接主服務器之前睡眠的秒數。如果主服務器.info文件中的值可以讀取則優先使用。如果未設置,默認值為 60。通常配置以上 2 個參數可以減少網絡問題導致的主從數據同步延遲。
一般網絡問題的錯誤是:
[ERROR] Error reading packet from server: Lost connection to MySQL server during query
(server_errno=xxxx)
[ERROR] Slave I/O thread: Failed reading log event, reconnecting to retry, log ‘edu-mysqlbin.000256’ position 23456
正式環境中主從配置推薦的my.cnf
Master
[client]
port = 3306
socket = /usr/local/mysql/mysql.sock
[mysqld]
character-set-server = utf8
collation-server = utf8_general_ci
skip-external-locking
skip-name-resolve
user = mysql
port = 3306
basedir = /usr/local/mysql
datadir = /home/mysql/data
tmpdir = /home/mysql/temp
# server_id = .....
socket = /usr/local/mysql/mysql.sock
log-error = /home/mysql/logs/mysql_error.log
pid-file = /home/mysql/mysql.pid
open_files_limit = 10240
back_log = 600
max_connections=500
max_connect_errors = 6000
wait_timeout=605800
#open_tables = 600
#table_cache = 650
#opened_tables = 630
max_allowed_packet = 32M
sort_buffer_size = 4M
join_buffer_size = 4M
thread_cache_size = 300
query_cache_type = 1
query_cache_size = 256M
query_cache_limit = 2M
query_cache_min_res_unit = 16k
tmp_table_size = 256M
max_heap_table_size = 256M
key_buffer_size = 256M
read_buffer_size = 1M
read_rnd_buffer_size = 16M
bulk_insert_buffer_size = 64M
lower_case_table_names=1
default-storage-engine = INNODB
innodb_buffer_pool_size = 2G
innodb_log_buffer_size = 32M
innodb_log_file_size = 128M
innodb_flush_method = O_DIRECT
#####################
thread_concurrency = 32
long_query_time= 2
slow-query-log = on
slow-query-log-file = /home/mysql/logs/mysql-slow.log
## replication
server_id=205
#binlog-do-db=roncoo
binlog-ignore-db=mysql
log-bin=edu-mysql-bin
binlog_cache_size=1M
binlog_format=mixed
expire_logs_days=7
slave_skip_errors=1062
[mysqldump]
quick
max_allowed_packet = 32M
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
Slave
[client]
port = 3306
socket = /usr/local/mysql/mysql.sock
[mysqld]
character-set-server = utf8
collation-server = utf8_general_ci
skip-external-locking
skip-name-resolve
user = mysql
port = 3306
basedir = /usr/local/mysql
datadir = /home/mysql/data
tmpdir = /home/mysql/temp
# server_id = .....
socket = /usr/local/mysql/mysql.sock
log-error = /home/mysql/logs/mysql_error.log
pid-file = /home/mysql/mysql.pid
open_files_limit = 10240
back_log = 600
max_connections=500
max_connect_errors = 6000
wait_timeout=605800
#open_tables = 600
#table_cache = 650
#opened_tables = 630
max_allowed_packet = 32M
sort_buffer_size = 4M
join_buffer_size = 4M
thread_cache_size = 300
query_cache_type = 1
query_cache_size = 256M
query_cache_limit = 2M
query_cache_min_res_unit = 16k
tmp_table_size = 256M
max_heap_table_size = 256M
key_buffer_size = 256M
read_buffer_size = 1M
read_rnd_buffer_size = 16M
bulk_insert_buffer_size = 64M
lower_case_table_names=1
default-storage-engine = INNODB
innodb_buffer_pool_size = 2G
innodb_log_buffer_size = 32M
innodb_log_file_size = 128M
innodb_flush_method = O_DIRECT
#####################
thread_concurrency = 32
long_query_time= 2
slow-query-log = on
slow-query-log-file = /home/mysql/logs/mysql-slow.log
## replication
server_id=206
#binlog-do-db=roncoo
binlog-ignore-db=mysql
log-bin=edu-mysql-slave1-bin
binlog_cache_size = 1M
binlog_format=mixed
expire_logs_days=7
slave_skip_errors=1062
relay_log=edu-mysql-relay-bin
log_slave_updates=1
read_only=1
[mysqldump]
quick
max_allowed_packet = 32M
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid