Mysql內建的複製功能是構建大型、高性能應用程序的基礎。複製功能不僅有利於構建高性能的應用,同時也是高可用性、可擴展性、災難恢復、備份以及數據倉庫等工作的基礎。
一、複製概述
1、複製的功用
數據分佈、負載均衡、備份、高可用性、MySQL升級測試
2、複製的工作原理
①主庫把數據更改記錄到二進制日誌(binary log)中
②備庫將主庫的二進制日誌複製到本地的中繼日誌(relay log)中。首先,備庫會啓動一個I/O線程,跟主庫建立一個普通的客戶端連接,然後在主庫上啓動一個特殊的二進制轉儲(binlog dump)線程,此轉儲線程會讀取主庫上二進制日誌中的事件。如果該線程追趕上了主庫,則進入睡眠狀態,直到主庫發送信號通知其有新的事件產生時纔會被喚醒,備庫I/O線程會將接收到的事件記錄到中繼日誌中。
③備庫的SQL線程從中繼日誌中讀取事件本執行,從而實現備庫數據更新。
3、複製方式
①基於語句的複製:實際上是把主庫上執行的SQL語句在從庫上重放一遍,因此效率高,佔用帶寬小,但不如基於行的複製精確,對於不確定性的語句(例如包含時間函數的語句)會有問題。另外這種複製是串行的,爲了保證串行執行,需要加更多的鎖。
②基於行的複製:此時二進制日誌記錄的是數據本身,這無疑會增加網絡帶寬消耗和I/O線程負載,優點是從庫無需sql語句重放,但無法判斷執行了哪些SQL語句
③混合模式,即上面兩種方式的組合
mysql默認基於語句複製,建議採用基於行的複製;
4、mysql複製常用架構
一主一從
一主多從:常用於寫操作不頻繁,查詢量較大的環境中
主主互備:即兩臺mysql server互相將對方作爲自己的master,避免了單點故障,主要用於對mysql寫操作比較頻繁的環境中
多源複製:即slave服務器可指向多個master服務器;MySQL 5.7和MariaDB 10支持,主要用於對mysql讀寫量都比較大的環境中
★5、特別說明
mysql複製集羣中,每個server的Server ID必須唯一;
集羣中如果有兩個或更多的master,只允許對不同的數據庫進行寫操作,否則在複製時會產生混亂;
slave服務器只許讀,不許寫;
建議使用InnoDB作爲默認存儲引擎,基於語句複製
二、主從複製
1、版本:從節點版本不能低於主節點
2、從哪裏開始複製
⑴從0開始:適用於主從均爲新建立的服務器;
⑵如果主服務器已經運行一段時間且存在不小的數據量:
①完全備份主服務器數據,並將數據恢復至從服務器;
mysqldump --single-transaction --all-databases --master-data=2 --host=SERVER1 | mysql --host=SERVER2
--master-data:該選項將binlog的位置和文件名追加到輸出文件中。如果爲1,將會輸出CHANGE MASTER TO命令;如果爲2,則在輸出的CHANGE MASTER TO命令前添加註釋。該選項會打開--lock-all-tables選項,除非另外指定--single-transaction。
②從服務器從備份時主服務器二進制日誌所在位置開始複製;
3、配置過程
⑴master:
①啓用二進制日誌
②定義server-id
③創建有複製權限的賬號:
GRANT REPLICATION SLAVE ON *.* TO repluser@'192.168.30.%' IDENTIFIED BY 'replp@ss';
FLUSH PRIVILEGES;
⑵slave:
①啓用中繼日誌
②定義server-id
③使用有複製權限的賬號連接master
CHANGE MASTER TO option [, option] ...
option:
MASTER_BIND = 'interface_name'
| MASTER_HOST = 'host_name' # 指明要連接的主節點
| MASTER_USER = 'user_name' # 具有複製權限的賬號
| MASTER_PASSWORD = 'password' # 上述用戶的密碼
| MASTER_PORT = port_num
| MASTER_CONNECT_RETRY = interval
| MASTER_HEARTBEAT_PERIOD = interval
| MASTER_LOG_FILE = 'master_log_name' # 複製起點,主節點上的二進制日誌
| MASTER_LOG_POS = master_log_pos # 複製起點,主節點上二進制日誌中的事件位置
......
例如:CHANGE MASTER TO MASTER_HOST='172.16.100.7',MASTER_USER='repluser',MASTER_PASSWORD='replpass',MASTER_LOG_FILE='master-bin.000001',MASTER_LOG_POS=495;
④啓動io thread以及sql thread:START SLAVE;
也可單獨啓動IO_THREAD或者SQL_THREAD線程,如START SLAVE IO_THREAD;
⑤查看從節點的狀態:SHOW SLAVE STATUS\G
⑥查看線程狀態:SHOW PROCESSLIST;
⑦相關文件:
master.info:記錄了主庫帳號信息和I/O線程當前讀取到主庫的二進制文件的位置
relay-log.info:記錄從庫的SQL線程當前讀取到中繼日誌的位置
⑧從庫如果不做級連複製或者備份,就不要開啓二進制日誌(在配置文件中添加log_bin=off)
4、複製中要注意的問題
⑴如何限制從服務器只讀?
①更改slave的全局服務器變量read-only爲yes:
動態:SET GLOBAL read_only = on;
靜態:編輯配置文件
[mysqld]
read_only = on
注意:此限制對於擁有super權限的用戶無效
②阻止所有用戶執行寫操作:FLUSH TABLES WITH READ LOCK;
⑵如何保證複製的“安全”?
master:
設置參數:sync_binlog = 1
mysql處理commit語句,它將整個事務寫入二進制日誌並將事務提交給存儲引擎中,如果開啓該選項,mysql每次在提交事務給存儲引擎之前會將二進制日誌同步到磁盤上,保證在服務器崩潰時不會丟失事件。
如果使用InnoDB存儲引擎:
innodb_flush_log_at_trx_commit = 1 (默認啓用)
innodb_support_xa = on (默認啓用)
slave:
sync_master_info = 1
sync_relay_log = 1
sync_relay_log_info = 1
說明:
slave從master複製binary log寫到relay log,指的是先寫到 “OS cache”的relay-log,而不是馬上刷新到磁盤上,什麼時候刷新到磁盤還依賴於cache刷新時間。sync_relay_log #就表示每寫入幾次,觸發一次文件同步,其它同理;這三個選項是從MySQL 5.5纔開始引進的
以下示例中,主庫node1: 192.68.30.10,從庫node2: 192.68.30.20;使用MySQL 5.6.30
主庫:
[root@node1 ~]# vim /etc/my.cnf [mysqld] basedir = /usr/local/mysql datadir = /mydata/data port = 3306 server_id = 1 socket = /tmp/mysql.sock skip-name-resolve log-bin = /mydata/binlogs/master-bin binlog_format = row sync_binlog = 1 default_storage_engine = innodb #從MySQL 5.5開始默認的存儲引擎就是InnoDB,這項可不設置 [root@node1 ~]# service mysqld start Starting MySQL SUCCESS! [root@node1 ~]# mysql < hellodb.sql #生成一些數據 [root@node1 ~]# mysql ... mysql> prompt master PROMPT set to 'master> ' master> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | hellodb | | mysql | | performance_schema | | test | +--------------------+ 5 rows in set (0.00 sec) #創建具有複製權限的賬號: master> grant replication slave on *.* to repluser@'192.168.30.%' identified by 'replp@ss'; Query OK, 0 rows affected (0.01 sec) master> flush privileges; Query OK, 0 rows affected (0.00 sec) master> exit Bye [root@node1 ~]# mysqldump --single-transaction --all-databases --master-data=2 > msback.sql #完全備份 [root@node1 ~]# scp msback.sql root@node2:/root/ msback.sql 100% 640KB 640.1KB/s 00:00
從庫:
[root@node2 ~]# vim /etc/my.cnf [mysqld] basedir = /usr/local/mysql datadir = /mydata/data port = 3306 server_id = 2 #要與主庫不同 socket = /tmp/mysql.sock skip-name-resolve log-bin = /mydata/binlogs/slave-bin #如果不是基於GTID複製或不打算讓slave充當其它slave的master,可不用開啓 binlog_format = row sync_binlog = 1 relay-log = /mydata/relaylogs/slave-relay #啓用中繼日誌 sync_master_info = 1 sync_relay_log = 1 sync_relay_log_info = 1 read_only = on #只讀 [root@node2 ~]# service mysqld start Starting MySQL SUCCESS! [root@node2 ~]# mysql < msback.sql #先將主庫的備份數據恢復至從庫 [root@node2 ~]# grep "^-- CHANGE" msback.sql #查找主庫備份那一刻的二進制日誌文件位置 -- CHANGE MASTER TO MASTER_LOG_FILE='master-bin.000004', MASTER_LOG_POS=120; [root@node2 ~]# mysql mysql> prompt slave> PROMPT set to 'slave> ' slave> change master to master_host='192.168.30.10',master_user='repluser',master_password='replp@ss',\ -> master_log_file='master-bin.000004',master_log_pos=120; Query OK, 0 rows affected, 2 warnings (0.26 sec) slave> start slave; Query OK, 0 rows affected (0.01 sec) slave> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.30.10 Master_User: repluser Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000004 Read_Master_Log_Pos: 120 #io thread當前讀取到主庫的二進制文件的位置 Relay_Log_File: slave-relay.000002 Relay_Log_Pos: 284 Relay_Master_Log_File: master-bin.000004 Slave_IO_Running: Yes #io thread線程的狀態 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: 120 Relay_Log_Space: 453 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服務器的時長 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 #主庫的server_id Master_UUID: 5b1eb4fa-270c-11e6-917c-000c2940359d Master_Info_File: /mydata/data/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it 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 1 row in set (0.00 sec) [root@node2 ~]# cat /mydata/data/master.info #master.info文件的初始數據是由change master命令生成的 23 master-bin.000004 120 #一旦複製了新的日誌,master_log_pos的值就會更新 repluser replp@ss 3306 60 0
測試:
master> create database testdb; Query OK, 1 row affected (0.00 sec) master> use testdb Database changed master> create table students (Name char(30),Gender enum('m','f')); Query OK, 0 rows affected (0.03 sec)
slave> show databases; +--------------------+ | Database | +--------------------+ ... | testdb | +--------------------+ 6 rows in set (0.00 sec) slave> show tables from testdb; +------------------+ | Tables_in_testdb | +------------------+ | students | +------------------+ 1 row in set (0.00 sec) slave> show processlist; +----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------------+------------------+ | 2 | system user | | NULL | Connect | 5759 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL | | 3 | root | localhost | NULL | Query | 0 | init | show processlist | | 4 | system user | | NULL | Connect | 5759 | Waiting for master to send event | NULL | +----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------------+------------------+ 3 rows in set (0.00 sec) [root@node2 ~]# cat /mydata/data/master.info 23 master-bin.000004 356 192.168.30.10 repluser replp@ss 3306 60 0
5、半同步複製
MySQL 5.5之前的複製都是異步的,主服務器在將更新操作寫入二進制日誌文件中後,不用管從服務器是否已經完成複製,就可以自由處理其它事務處理請求。異步複製能提供較高的性能,但無疑易造成主/從服務器數據的不一致。
MySQL 5.5開始引入半同步複製功能,此功能是由google開發的一個插件實現的。半同步複製要求主庫提交的每一個事務,至少有一個備庫成功接收後,才能繼續提交下一個。
半同步複製的概念詳解:
①當slave主機連接到master時,能夠查看其是否已開啓半同步複製功能。
②當master上開啓半同步複製的功能時,至少應該有一個slave開啓此功能。此時,一個線程在master上提交事務將受到阻塞,直到得知一個已開啓半同步複製功能的slave已收到此事務的所有事件,或等待超時。
③當一個事務的事件都已寫入relay-log中且已刷新到磁盤,slave纔會告知已收到。在 master實例上,有一個專門的線程(ack_receiver)接收備庫的響應消息。
④如果等待超時,也就是master沒被告知已收到,此時master會自動轉換爲異步複製模式。當至少一個半同步的slave趕上了,master與其slave自動轉換爲半同步複製。
⑤半同步複製的功能要在master,slave都開啓,若只開啓一邊,它依然爲異步複製。
master:
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SHOW GLOBAL VARIABLES LIKE '%semi%';
SET GLOBAL rpl_semi_sync_master_enabled = on;
SET GLOBAL rpl_semi_sync_master_timeout = 5000; #等待從服務器確認的超時時長,單位爲毫秒,超時則轉爲異步模式
[mysqld]
rpl_semi_sync_master_enabled = on
rpl_semi_sync_master_timeout = 5000
SHOW GLOBAL STATUS LIKE 'rpl_semi%';
slave:
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = on;
然後只需重啓io thread:
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;
[mysqld]
rpl_semi_sync_slave_enabled = on
主庫: master> install plugin rpl_semi_sync_master soname 'semisync_master.so'; Query OK, 0 rows affected (0.20 sec) master> show global variables like '%semi%'; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_enabled | OFF | | rpl_semi_sync_master_timeout | 10000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_no_slave | ON | +------------------------------------+-------+ 4 rows in set (0.00 sec) master> set global rpl_semi_sync_master_enabled = on; Query OK, 0 rows affected (0.01 sec) master> set global rpl_semi_sync_master_timeout = 5000; Query OK, 0 rows affected (0.00 sec) master> show global variables like '%semi%'; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_enabled | ON | | rpl_semi_sync_master_timeout | 5000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_no_slave | ON | +------------------------------------+-------+ 4 rows in set (0.00 sec) [root@node1 ~]# vim /etc/my.cnf [mysqld] ... rpl_semi_sync_master_enabled = on rpl_semi_sync_master_timeout = 5000
從庫: slave> install plugin rpl_semi_sync_slave soname 'semisync_slave.so'; Query OK, 0 rows affected (0.11 sec) slave> show global variables like '%semi%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | rpl_semi_sync_slave_enabled | OFF | | rpl_semi_sync_slave_trace_level | 32 | +---------------------------------+-------+ 2 rows in set (0.00 sec) slave> set global rpl_semi_sync_slave_enabled = on; Query OK, 0 rows affected (0.00 sec) slave> stop slave io_thread; Query OK, 0 rows affected (0.01 sec) slave> start slave io_thread; Query OK, 0 rows affected (0.00 sec) slave> show global status like 'rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+ 1 row in set (0.00 sec) [root@node2 mydata]# vim /etc/my.cnf [mysqld] ... rpl_semi_sync_slave_enabled = on
主庫: master> show global status like 'rpl_semi%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | #已連接的啓用了半同步複製的從服務器數量 | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 0 | | Rpl_semi_sync_master_no_times | 0 | | Rpl_semi_sync_master_no_tx | 0 | | Rpl_semi_sync_master_status | ON | #服務器當前是否工作於半同步複製模式 | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 0 | | Rpl_semi_sync_master_tx_wait_time | 0 | | Rpl_semi_sync_master_tx_waits | 0 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 0 | #表示通過半同步複製到從庫的事務數 +--------------------------------------------+-------+ 14 rows in set (0.00 sec)
6、發送複製事件到其它從庫
MySQL支持多級複製,即一個slave可以充當其它服務器的master。這需要開啓log_slave_updates選項,在開啓該選項後,MySQL會將其執行過的事件記錄到自己的二進制日誌中。這樣其它從庫就可以複製其二進制日誌。
複製級別不宜過多,以免數據落後太多。
可以讓一個從庫專門做分發主庫,而不用負責查詢工作,這時候可將它的存儲引擎修改爲blackhole(不生成任何實際數據)。
7、複製過濾器
讓slave僅複製有限的幾個數據庫,甚至於僅複製某數據庫內有限的幾張表的機制;
有兩種方案:
⑴在主節點上過濾:在向二進制日誌記錄事件時,僅記錄指定數據庫的相關操作;
binlog_do_db = # 數據庫白名單
binlog_ignore_db = # 數據庫黑名單
⑵在從節點上過濾:僅從中繼日誌中讀取指定的數據庫或表的相關事件並應用於本地;
replicate_do_db =
replicate_ignore_db =
replicate_db_table = DB_NAME.TB_NAME
replicate_ignore_table =
replicate_wild_do_table = #可使用通配符
replicate_wild_ignore_table =
複製過濾容易出問題,不到萬不得已,不建議使用;如果實在要使用,建議在從節點上過濾
8、基於ssl的複製
⑴配置主庫爲CA服務器
[root@node1 ~]# cd /etc/pki/CA [root@node1 CA]# (umask 077;openssl genrsa -out private/cakey.pem 2048) #生成密鑰對 Generating RSA private key, 2048 bit long modulus ....................+++ ............................+++ e is 65537 (0x10001) [root@node1 CA]# openssl req -new -x509 -key private/cakey.pem -out cacert.pem -days 3650 #CA自簽證書 ... Country Name (2 letter code) [XX]:CN State or Province Name (full name) []:Zhejiang Locality Name (eg, city) [Default City]:Hangzhou Organization Name (eg, company) [Default Company Ltd]:Dongpu Organizational Unit Name (eg, section) []:tech Common Name (eg, your name or your server's hostname) []:ca.dongpu.com Email Address []:[email protected] [root@node1 CA]# touch index.txt serial [root@node1 CA]# echo 01 > serial [root@node1 CA]# mkdir csr
⑵爲主服務器創建證書
[root@node1 CA]# mkdir /usr/local/mysql/ssl [root@node1 CA]# cd /usr/local/mysql/ssl [root@node1 ssl]# (umask 077;openssl genrsa -out master.key 2048) Generating RSA private key, 2048 bit long modulus .......................+++ ...................+++ e is 65537 (0x10001) [root@node1 ssl]# openssl req -new -key master.key -out master.csr #生成證書籤署請求 ... Country Name (2 letter code) [XX]:CN State or Province Name (full name) []:Zhejiang Locality Name (eg, city) [Default City]:Hangzhou Organization Name (eg, company) [Default Company Ltd]:Dongpu Organizational Unit Name (eg, section) []:tech Common Name (eg, your name or your server's hostname) []:[email protected] Email Address []:[email protected] ... [root@node1 ssl]# openssl ca -in master.csr -out master.crt -days 1000 #CA簽署證書 ... 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated [root@node1 ssl]# cp /etc/pki/CA/cacert.pem ./ [root@node1 ssl]# ls cacert.pem master.crt master.csr master.key [root@node1 ssl]# chown -R mysql.mysql . [root@node1 ssl]# ll #注意,要確保密碼文件、證書文件等對於mysql用戶可讀 total 20 -rw-r--r-- 1 mysql mysql 1464 Jun 1 01:30 cacert.pem -rw-r--r-- 1 mysql mysql 4686 Jun 1 02:31 master.crt -rw-r--r-- 1 mysql mysql 1074 Jun 1 02:31 master.csr -rw------- 1 mysql mysql 1675 Jun 1 03:27 master.key
⑶爲從服務器創建證書,參考上述步驟,略
[root@node2 ssl]# ls cacert.pem slave.crt slave.csr slave.key
⑷在主庫上啓用ssl認證,並指定有複製權限的用戶要以ssl的方式連接
mysql> show global variables like '%ssl%'; +---------------+----------+ | Variable_name | Value | +---------------+----------+ | have_openssl | DISABLED | | have_ssl | DISABLED | | ssl_ca | | | ssl_capath | | | ssl_cert | | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | | +---------------+----------+ 9 rows in set (0.04 sec) [root@node1 ~]# vim /etc/my.cnf [mysqld] ... ssl_ca = /usr/local/mysql/ssl/cacert.pem ssl_cert = /usr/local/mysql/ssl/master.crt ssl_key = /usr/local/mysql/ssl/master.key [root@node1 ~]# service mysqld restart Shutting down MySQL.. SUCCESS! Starting MySQL.. SUCCESS! mysql> show global variables like '%ssl%'; +---------------+---------------------------------+ | Variable_name | Value | +---------------+---------------------------------+ | have_openssl | YES | | have_ssl | YES #顯示已啓用 | | ssl_ca | /usr/local/mysql/ssl/cacert.pem | | ssl_capath | | | ssl_cert | /usr/local/mysql/ssl/master.crt | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_key | /usr/local/mysql/ssl/master.key | +---------------+---------------------------------+ 9 rows in set (0.00 sec) mysql> revoke all privileges, grant option from repluser@'192.168.30.%'; Query OK, 0 rows affected (0.01 sec) mysql> grant replication slave,replication client on *.* to repluser@'192.168.30.%' identified by 'replp@ss' require ssl; Query OK, 0 rows affected (0.01 sec) # 在grant命令中指定require ssl選項 mysql> flush privileges; Query OK, 0 rows affected (0.02 sec)
⑸在從庫上也啓用ssl認證
[root@node2 ~]# vim /etc/my.cnf [mysqld] ... ssl_ca = /usr/local/mysql/ssl/cacert.pem ssl_cert = /usr/local/mysql/ssl/slave.crt ssl_key = /usr/local/mysql/ssl/slave.key [root@node2 ~]# service mysqld restart Shutting down MySQL.. SUCCESS! Starting MySQL.. SUCCESS! [root@node2 ~]# less /mydata/data/master.info 23 master-bin.000014 721 192.168.30.10 repluser replp@ss 3306 60 0 [root@node2 ~]# mysql #啓用ssl後需要在change master to命令中指定ssl相關選項重新指向主庫 mysql> change master to master_host='192.168.30.10',master_user='repluser',master_password='replp@ss',\ -> master_log_file='master-bin.000014',master_log_pos=721,\ -> master_ssl=1,\ -> master_ssl_ca='/usr/local/mysql/ssl/cacert.pem',\ -> master_ssl_cert='/usr/local/mysql/ssl/slave.crt',\ -> master_ssl_key='/usr/local/mysql/ssl/slave.key'; Query OK, 0 rows affected, 2 warnings (0.19 sec) mysql> start slave; Query OK, 0 rows affected (0.01 sec) mysql> show slave status\G *************************** 1. row *************************** ... Master_SSL_Allowed: Yes Master_SSL_CA_File: /usr/local/mysql/ssl/cacert.pem Master_SSL_CA_Path: Master_SSL_Cert: /usr/local/mysql/ssl/slave.crt Master_SSL_Cipher: Master_SSL_Key: /usr/local/mysql/ssl/slave.key ... Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it ... [root@node2 data]# vim master.info 23 master-bin.000024 120 192.168.30.10 repluser replp@ss 3306 60 1 #是否啓用ssl的標誌位 /usr/local/mysql/ssl/cacert.pem /usr/local/mysql/ssl/slave.crt /usr/local/mysql/ssl/slave.key 0 1800.000
9、爲從庫設定新的主庫
在從庫停止複製線程,而後重新設定CHANGE MASTER TO命令即可;
⑴計劃內提升一個從庫爲主庫:
①停止向老的主庫寫入數據;
②讓計劃提升爲主庫的從庫趕上主庫;
③提升從庫爲主庫
④修改其它從庫的指向
⑵計劃外提升一個從庫爲主庫:
①確定哪個從庫的數據爲最新最全;
②等待所有的從庫執行從主庫那複製而來的生成的中繼日誌;
③在提升爲主庫的從庫上STOP SLAVE;而後,讓各從庫指向新的主庫;
④再次比較主庫和各從庫上的兩個參數:
Master_Log_File,Read_Master_Log_Pos
三、雙主互備
(1)雙節點都得創建具有複製權限的用戶;
(2)雙節點都得啓用中繼日誌和二進制日誌;
(3)爲保證具有自動增長功能的字段能正確生成ID,需要配置兩個節點分別使用偶數或奇數ID號;
(4)互相把對方配置爲自己的主節點;
A節點:
[mysqld]
server-id=1
log-bin=mysql-bin
relay-log=mysql-relay
auto_increment_increment=2
auto_increment_offset=1
B節點:
[mysqld]
server-id=2
log-bin=mysql-bin
relay-log=mysql-relay
auto_increment_increment=2
auto_increment_offset=2
四、MySQL 5.6中關於複製的幾個新特性
1、master.info和relay-log.info支持存儲在表中:
master_info_repository = table
relay_log_info_repository = table
對應的表的名稱爲slave_master_info、slave_relay_log_info,位於mysql庫中
2、multi-threads slave
5.6以前的從服務器,一個io線程負責複製binary log,還有一個sql線程負責執行relay log中的sql語句。如果主服務器的數據更新相當頻繁,而從服務器由於某些原因跟不上,會導致從服務器落後比較長的時間。5.6之後可採用多個sql線程,每個sql線程處理不同的database,提高了併發性能。
slave_parallel_workers = # (0爲禁用,最大1024)
3、延時slave
在每個slave sql線程執行的時候都要等延遲時間到後進行下一個操作;
MASTER_DELAY=#,單位爲秒,在CHANGE MASTER TO命令中指定,例如:
CHANGE MASTER TO MASTER_DELAY=120
優點:在一定程度上防止了誤操作,比如說刪表等等;
可以一定程度上作爲有效的數據庫備份,無需再另行備份;
案例:誤刪了test庫中的test1表,沒有備份,只有延時slave
①先在主庫上查找刪除表的位置:
SHOW BINLOG EVENTS\G
假設刪除表的位置爲Log_name:master-bin.000001 Pos:884
②然後在從庫上:
STOP SLAVE;
CHANGE MASTER TO MASTER_DELAY = 0
START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE='mater-bin.000001',MASTER_LOG_POS=884;
4、GTID
⑴概述
GTID(global transaction identifier,全局事務標識)是對於一個已提交事務的編號,並且是一個全局唯一的編號,由UUID+TID組成的,其中UUID是一個MySQL實例的唯一標識,TID代表了該實例上已經提交的事務數量,並且隨着事務提交單調遞增。
例如:7800a22c-95ae-11e4-983d-080027de205a:10
GTID用來代替傳統的複製方法。不再使用binlog+pos開啓複製,而是使用master_auto_postion=1的方式自動匹配GTID斷點進行復制。
在傳統的slave端,binlog是不用開啓的,但是在GTID中,slave端的binlog必須開啓,目的是記錄執行過的GTID(強制)
⑵GTID的作用
假設有如上一個MySQL複製架構,Server A宕機,需要將業務切換到Server B上。同時,又需要將Server C的複製源改成Server B。複製源修改的命令語法很簡單即CHANGE MASTER TO MASTER_HOST='xxx', MASTER_LOG_FILE='xxx', MASTER_LOG_POS=#。而難點在於,由於同一個事務在每臺機器上所在的binlog名字和位置都不一樣,那麼找到Server C當前同步停止點所對應的Server B上的二進制日誌位置就成了難題。這種問題在MySQL 5.6的GTID出現後,就顯得非常的簡單。由於同一事務的GTID在所有節點上的值一致,那麼根據Server C當前停止點的GTID就能唯一定位到Server B上的GTID。甚至由於MASTER_AUTO_POSITION功能的出現,我們都不需要知道GTID的具體值,直接使用CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION命令就可完成failover的工作。
⑶GITD工作原理
①master更新數據時,會在事務前產生GTID,一同記錄到binlog日誌中。
②slave端的i/o線程將變更的binlog,寫入到本地的relay log中。
③sql線程從relay log中獲取GTID,然後對比slave端的binlog是否有記錄。
④如果有記錄,說明該GTID的事務已經執行,slave會忽略。
⑤如果沒有記錄,slave就會從relay log中執行該GTID的事務,並記錄到binlog。
⑷開啓GTID必須啓用如下幾項:
gtid_mode = on
enforce_gtid_consistency = on
log_slave_updates = on
這三個選項在主從服務器上都要啓用
其它可選項:
binlog_checksum = NONE or CRC32(默認爲CRC32)
每個session都會產生checksum值,並且寫入到binlog
master_verify_checksum = on
Master 當從binlog dump事件的時候會校驗checksum值
slave_sql_verify_checksum = on(默認已啓用)
SQL線程當從relay log讀取事件應用到slave之前會校驗checksum值
report_host = XXX
從庫向主庫報告的主機名或IP地址,可在主庫使用SHOW SLAVE HOSTS命令查看
report_port = #
從庫向主庫報告的連接端口號,默認爲3306
⑸啓用GTID後,從庫可這樣指向主庫:
CHANGE MASTER TO MASTER_HOST='master.magedu.com', MASTER_USER='repluser', MASTER_PASSWORD='replp@ss', MASTER_AUTO_POSITION=1;(不用指定具體的二進制日誌位置了)
⑹MariaDB中使用GTID需要做的修改:
①不支持的參數:
gtid-mode = on #MariaDB 10默認已啓用gtid
enforce-gtid-consistency = on
②修改的參數:
slave_parallel_workers參數修改爲slave_parallel_threads
③連接至主庫使用的命令:
一個新的參數:MASTER_USE_GTID={current_pos/slave_pos/no}
change master to master_host="127.0.0.1",master_port=3310,master_user="root",master_use_gtid=current_pos;
5、啓用GTID做主從複製示例
主庫:
[root@node1 ~]# vim /etc/my.cnf [mysqld] ... gtid_mode = on enforce_gtid_consistency = on log_slave_updates = on master_verify_checksum = on [root@node1 ~]# service mysqld restart Shutting down MySQL.. SUCCESS!
從庫:
[root@node2 ~]# vim /etc/my.cnf [mysqld] ... gtid_mode = on enforce_gtid_consistency = on log_slave_updates = on slave_parallel_workers = 3 #啓動三個SQL線程 report_host = 192.168.30.20 report_port = 3306 [root@node2 ~]# rm -f /mydata/data/master.info #刪除現有的master.info只是爲了測試後面change master命令中的master_auto_position選項是否能實現複製位置的自動確定 [root@node2 ~]# service mysqld restart Shutting down MySQL.. SUCCESS! [root@node2 ~]# mysql mysql> show global variables like '%gtid%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | binlog_gtid_simple_recovery | OFF | | enforce_gtid_consistency | ON | | gtid_executed | | | gtid_mode | ON | | gtid_owned | | | gtid_purged | | | simplified_binlog_gtid_recovery | OFF | +---------------------------------+-------+ 7 rows in set (0.00 sec) mysql> change master to master_host='192.168.30.10',master_user='repluser',master_password='replp@ss',\ -> master_ssl=1,\ -> master_ssl_ca='/usr/local/mysql/ssl/cacert.pem',\ -> master_ssl_cert='/usr/local/mysql/ssl/slave.crt',\ -> master_ssl_key='/usr/local/mysql/ssl/slave.key',\ -> master_auto_position=1; Query OK, 0 rows affected, 2 warnings (0.01 sec) mysql> start slave; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.30.10 Master_User: repluser Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000026 Read_Master_Log_Pos: 151 ...... Auto_Position: 1 #表示已開啓自動位置確定 1 row in set (0.00 sec) mysql> show processlist; #確實已啓動三個sql線程 +----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------------+------------------+ | 6 | root | localhost | NULL | Query | 0 | init | show processlist | | 7 | system user | | NULL | Connect | 68 | Waiting for master to send event | NULL | | 8 | system user | | NULL | Connect | 68 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL | | 9 | system user | | NULL | Connect | 68 | Waiting for an event from Coordinator | NULL | | 10 | system user | | NULL | Connect | 68 | Waiting for an event from Coordinator | NULL | | 11 | system user | | NULL | Connect | 68 | Waiting for an event from Coordinator | NULL | +----+-------------+-----------+------+---------+------+-----------------------------------------------------------------------------+------------------+ 6 rows in set (0.00 sec)
測試:
主庫: mysql> show slave hosts; #此命令可查看有哪些已連接的從庫 +-----------+---------------+------+-----------+--------------------------------------+ | Server_id | Host | Port | Master_id | Slave_UUID | +-----------+---------------+------+-----------+--------------------------------------+ | 2 | 192.168.30.20 | 3306 | 1 | f61472ed-2714-11e6-91b4-000c29bd6823 | +-----------+---------------+------+-----------+--------------------------------------+ 1 row in set (0.00 sec) mysql> create database wuxia; Query OK, 1 row affected (0.02 sec)
從庫: mysql> show databases; +--------------------+ | Database | +--------------------+ ...... | wuxia | +--------------------+
基於GTID的複製的其它相關問題可參考博客http://www.tuicool.com/articles/rua2emE
五、多源複製
MySQL 5.7.2和MariaDB 10開始支持多源複製,即一個slave可指向多個master;
以下是MariaDB 10中從庫指向多個主庫的語法:
CHANGE MASTER ['connection_name'] TO ....
FLUSH RELAY LOGS ['connection_name']
MASTER_POS_WAIT(....,['connection_name'])
RESET SLAVE ['connection_name'] [ALL]
SHOW RELAYLOG ['connection_name'] EVENTS
SHOW SLAVE ['connection_name'] STATUS
SHOW ALL SLAVES STATUS
START SLAVE ['connection_name'...]]
START ALL SLAVES ...
STOP SLAVE ['connection_name'] ...
STOP ALL SLAVES ...
示例:
CHANGE MASTER 'master1' TO MASTER_HOST= '192.168.30.10',MASTER_USER='repluser',MASTER_PASSWORD='replp@ss',MASTER_LOG_FILE='master-bin.000028',master_log_pos=1485;
CHANGE MASTER 'master2' TO MASTER_HOST= '192.168.30.20',MASTER_USER='repluser',MASTER_PASSWORD='replp@ss',MASTER_LOG_FILE='master-bin.000023',master_log_pos=639;
START ALL SLAVES;
SHOW ALL SLAVES STATUS\G
也可單獨操作某個connection:
START SLAVE 'master1';
SHOW SLAVE 'master1' STATUS\G