MySQL復制介紹
• MySQL復制允許將主實例(master)上的數據同步到一個或多個從實例(slave)上,默認情況下復制是異步進行的,從庫也不需要一直連接到主庫來同步數據
• MySQL復制的數據粒度可以是主實例上所有的數據庫,也可以是指定的一個或多個數據庫,也可以是一個數據庫里的指定的表
• MySQL復制所帶來的優勢在於:
• 擴展能力:通過復制功能可以將MySQL的性能壓力分擔到一個或多個slave上。這要求所有的寫操作和修改操作都必須在Master上完成,而讀操作可以被分配到一個或多個slave上。將讀寫分離到不同服務器執行之后,MySQL的讀寫性能得到提升
• 數據庫備份:由於從實例是同步主實例的數據,所以可以將備份作業部署到從庫
• 數據分析和報表:同樣,一些數據分析和報表的實現可以在從實例執行,以減少對主庫的性能影響
• 容災能力:可以在物理距離較遠的另一個數據中心建立一個slave,保證在主實例所在地區遭遇災難時,在另一個數據中心能快速恢復
MySQL復制有兩種方法:
• 傳統方式:基於主庫的bin-log將日志事件和事件位置復制到從庫,從庫再加以應用來達到主從同步的目的
• Gtid方式:global transaction identifiers是基於事務來復制數據,因此也就不依賴日志文件,同時又能更好的保證主從庫數據一致性
MySQL復制有多種類型:
• 異步復制:一個主庫,一個或多個從庫,數據異步同步到從庫
• 同步復制:在MySQL Cluster中特有的復制方式
• 半同步復制:在異步復制的基礎上,確保任何一個主庫上的事務在提交之前至少有一個從庫已經收到該事務並日志記錄下來
• 延遲復制:在異步復制的基礎上,人為設定主庫和從庫的數據同步延遲時間,即保證數據延遲至少是這個參數
MySQL基於binlog的復制
復制的工作原理是數據庫修改事件記錄到bin log中並傳遞到slave,然后slave在本地還原的過程。而事件記錄到bin log的格式會有所不同。
MySQL復制有三種核心格式
• 基於語句的復制(statement based replication):基於主庫將SQL語句寫入到bin log中完成復制
• 基於行數據的復制(row based replication):基於主庫將每一個行數據變化的信息作為事件寫入到bin log中完成日志
• 混合復制(mixed based replication):上述兩者的結合。默認情況下優先使用基於語句的復制,只有當部分語句如果基於語句復制不安全的情況下才會自動切換為基於行數據的復制
mysql> show variables like '%binlog_format%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+
基於binary log的復制是指主庫將修改操作寫入到bin log中,從庫負責讀取主庫的bin log,並在本地復制一份,然后將里面的操作在從庫執行一遍
每個從庫會保存目前讀取主庫日志的文件名和日志位置
主庫和每個從庫都必須有一個唯一ID,叫server-id配置在配置文件中
配置方法:
• 5.7主庫需要開啟bin-log,並且指定一個唯一的server-id,重啟數據庫
mysql> show variables like '%log_bin%'; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | log_bin | OFF | | log_bin_basename | | | log_bin_index | | | log_bin_trust_function_creators | OFF | | log_bin_use_v1_row_events | OFF | | sql_log_bin | ON | +---------------------------------+-------+ mysql> show variables like '%server_id%'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | server_id | 6 | | server_id_bits | 32 | +----------------+-------+
[mysqld] %% log-bin=mysql-bin server-id=1
• 8.0中binlog是默認開啟的,且server_id默認為1:
mysql> show variables like '%log_bin%'; +---------------------------------+-------------------------+ | Variable_name | Value | +---------------------------------+-------------------------+ | log_bin | ON | | log_bin_basename | /data/3306/binlog | | log_bin_index | /data/3306/binlog.index | | log_bin_trust_function_creators | OFF | | log_bin_use_v1_row_events | OFF | | sql_log_bin | ON | +---------------------------------+-------------------------+ mysql> show variables like '%server_id%'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | server_id | 6 | | server_id_bits | 32 | +----------------+-------+
在同一個復制組下的所有實例的server_id都必須是唯一的,而且取值必須是正整數,取值范圍是1~(232)−1
確保主庫的my.cnf中skip-networking參數為非開啟狀態,否則會導致主從庫不能通信而復制失敗
• 在主庫創建一個專門用來做復制的數據庫用戶,這樣所有從庫都可以用這個用戶來連接主庫,也可以確保這個用戶只有復制的權限雖然可以用任何擁有復制權限的MySQL用戶來建立復制關系,但由於被使用的用戶名和密碼會明文保存在備庫的master.info文件中,所以為安全起見,最好是使用僅有復制權限的獨立用戶
# 5.7 create user 'repl'@'10.0.0.%' identified by 'mysql'; grant replication slave on *.* to 'repl'@'10.0.0.%';
# 8.0
create user 'repl'@'10.0.0.%' identified with mysql_native_password by 'mysql'; grant replication slave on *.* to 'repl'@'10.0.0.%';
• 從slave庫驗證遠程連接主庫是否正常
[root@slave1 ~]# mysql -urepl -pmysql -h10.0.0.101 Server version: 5.7.28-log MySQL Community Server (GPL) [root@slave1 ~]# mysql -urepl -pmysql -h10.0.0.201 Server version: 8.0.13 MySQL Community Server - GPL
• 獲取主庫的日志信息
為了確保建立的備庫能從正確的bin log位置開啟復制,要首先獲取主庫的bin log信息,包括當前的日志文件名和日志文件內的位置
mysql> flush tables with read lock; ## 主庫上所有表加鎖,停止修改 mysql> show master status; ## 獲取主庫的日志信息,file表示當前日志,position表示當前日志里的位置 +---------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +---------------+----------+--------------+------------------+-------------------+ | binlog.000017 | 155 | | | | +---------------+----------+--------------+------------------+-------------------+ mysql> drop table temp; ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock mysql> create database course3; ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock mysql> insert into temp values(1,2); ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock [root@master1 /data/3306]# mysqlbinlog -v binlog.000015 # at 124 #200814 19:24:09 server id 6 end_log_pos 155 CRC32 0x2afb786e Previous-GTIDs # [empty] # at 155
• 主庫數據生成鏡像並上傳到從庫兩種方式生成鏡像:
一種是用mysqldump,是innodb存儲引擎推薦的方式;
一種是將數據文件從主庫拷貝到從庫,這種方式效率更高(省去了dump/import過程中insert語句執行導致的更新index的行為),但innodb不推薦使用
a) mysqldump --all-databases --master-data -u root -p -P 3308 > dbdump.db ##mysqldump方式導出所有數據庫數據到dbdump.db文件,--master-data表示導出數據直接加上change master to參數以便備庫使用 b) 如果使用文件拷貝的辦法: 將主庫臨時關閉,並將相關文件拷貝到從庫上 master: sftp -oPort=22 root@10.0.0.203 sftp> put dbdump.db 或者slave: sftp -oPort=22 root@10.0.0.201 sftp> get dbdump.db
• 主庫釋放鎖
mysql> UNLOCK TABLES;
• 從庫配置唯一server-id,並重啟mysql實例
從庫的bin log屬性可以打開也可以不打開
vim /etc/my.cnf [mysqld] server_id=3 cd /data/3306 rm -rf auto.cnf reboot #出現錯誤時,這樣更有效 /etc/init.d/mysqld restart
dbdump.db文件內容: DROP TABLE IF EXISTS `rcx`; /*!4 SET @saved_cs_client = @@character_set_client */; /*!4 SET character_set_client = utf8 */; CREATE TABLE `rcx` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` char(3) DEFAULT NULL, `d` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 /*!50500 PARTITION BY RANGE COLUMNS(a,d,c) (PARTITION p0 VALUES LESS THAN (5,10,'ggg') ENGINE = InnoDB, PARTITION p1 VALUES LESS THAN (10,20,'mmm') ENGINE = InnoDB, PARTITION p2 VALUES LESS THAN (15,30,'sss') ENGINE = InnoDB, PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE) ENGINE = InnoDB) */; /*!4 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `rcx` -- LOCK TABLES `rcx` WRITE; /*!40000 ALTER TABLE `rcx` DISABLE KEYS */; INSERT INTO `rcx` VALUES (4,5,'abc',1),(5,9,'abc',1),(4,11,'ggg',1),(5,11,'abc',1),(4,5,'abc',1),(5,9,'abc',1),(4,11,'ggg',1),(5,11,'abc',1),(6,2,'a bc',1); /*!40000 ALTER TABLE `rcx` ENABLE KEYS */; UNLOCK TABLES;
• 從庫應用主庫的數據鏡像
Mysqldump的鏡像,通過source命令執行原始文件拷貝的鏡像,將文件復制到和主庫相同的目錄下
source dbdump.db
• 從庫指定主庫的日志信息和鏈接信息
mysql> help change master to
CHANGE MASTER TO MASTER_HOST=‘master_host_name’, ##主庫的主機名 MASTER_PORT=port_number ##主庫的端口號 MASTER_USER=‘replication_user_name’, ##復制的數據庫用戶名 MASTER_PASSWORD=‘replication_password’, ##復制的用戶密碼 MASTER_LOG_FILE=‘recorded_log_file_name’, ##主庫的日志文件名 MASTER_LOG_POS=recorded_log_position; ##主庫的日志文件位置
CHANGE MASTER TO MASTER_HOST='10.0.0.201', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='mysql', MASTER_LOG_FILE='binlog.000017',
MASTER_LOG_POS=155;
• 從庫啟動復制進程
mysql> START SLAVE;
• 查看主備庫復制是否正常:
• 在slave上執行show slave status\G命令
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.0.0.201 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: binlog.000017 Read_Master_Log_Pos: 155 Relay_Log_File: slave1-relay-bin.000002 Relay_Log_Pos: 319 Relay_Master_Log_File: binlog.000017 Slave_IO_Running: Yes 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: 155 Relay_Log_Space: 528 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_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: 6 Master_UUID: c74e4b27-d5f6-11ea-bbe0-000c29396a9d Master_Info_File: mysql.slave_master_info 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 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 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: Master_public_key_path: Get_master_public_key: 0
• 驗證復制工作正常:基本方法是在主庫添加任意數據,查看從庫是否能查詢到
• 在Master上執行:
create table temp3(id int,name varchar(10)); insert into temp3 values(1,'a'),(2,'b');
• 在Slave上驗證:
mysql> select * from temp3; +------+------+ | id | name | +------+------+ | 1 | a | | 2 | b | +------+------+
• 簡單排錯:
• Last_IO_Errno: 1593 • Last_IO_Error: Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work. • 解決辦法:刪除備庫的auto.cnf文件,重啟mysql,生成新的UUID • Last_IO_Errno: 2003 • Last_IO_Error: error connecting to master 'repl@192.168.237.128:3306' - retry-time: 60 retries: 19 • 解決辦法:備庫連接主庫失敗,檢查防火牆,用戶密碼端口是否設置正確
• 復制過程中由於備份之前沒有lock全表而導致的數據復制異常
• 主庫上執行:
Delimiter // Create procedure proc1() Begin Declare n int default 1; while n<=20000 do Insert into temp2 values(n, 'mike'); Set n=n+1; End while; End; // delimiter ; show master status; call proc1();
• 存儲過程執行過程中開始mysqldump,並建立從庫
MySQL基於binlog的多slave環境
當第一個slave創建好之后,如果還想創建其他的slave,則可以直接使用先前使用的備份文件,分別執行:
a) 在slave的my.cnf上分配新的server_id
b) 從庫應用主庫的數據鏡像
a) 利用相同的change master命令將從庫指定主庫的日志信息和鏈接信息
c) slave start
這樣第二個slave也就創建起來了
主庫上執行:
mysql> show processlist; +----+-----------------+------------------+--------+-------------+-------+---------------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-----------------+------------------+--------+-------------+-------+---------------------------------------------------------------+------------------+ | 4 | event_scheduler | localhost | NULL | Daemon | 49740 | Waiting on empty queue | NULL | | 11 | root | localhost | course | Query | 0 | starting | show processlist | | 13 | repl | 10.0.0.203:33868 | NULL | Binlog Dump | 5168 | Master has sent all binlog to slave; waiting for more updates | NULL | | 17 | repl | 10.0.0.204:50572 | NULL | Binlog Dump | 120 | Master has sent all binlog to slave; waiting for more updates | NULL | +----+-----------------+------------------+--------+-------------+-------+---------------------------------------------------------------+------------------+
如果想在事后再增加一個slave,但之前的備份文件已經不存在,或者主庫的日志文件已經被清除了的情況下,考慮使用如下辦法:
在已經建立好的復制環境中新增一個從庫,則不需要關閉主庫復制數據,而是用已有的從庫復制數據即可
a) 關閉現有的從庫
[root@slave1 ~]# /etc/init.d/mysqld stop
[root@slave2 ~]# /etc/init.d/mysqld stop
### mysqladmin shutdown
b) 拷貝從庫的文件到新的從庫,包括log文件和relay log文件,其中如果relay log使用了從庫的主機名,則需要調relay-log-index參數
slave1: cd /data tar -zcvf 3306.tar.gz 3306 sftp -oPort=22 root@10.0.0.203 sftp> put 3306.tar.gz
/etc/init.d/mysql.server start
slave2: cd /data mv 3306 3306_bak mv /root/3306.tar.gz . tar -zxvf 3306.tar.gz
rm -rf ./3306/auto.cnf
b) 拷貝master info和relay log info文件到新的從庫
c) 為新的從庫分配一個唯一的server-id
vim /etc/my.cnf [mysqld] server_id=4
/etc/init.d/mysqld start
d) 新的從庫啟動slave進程
set sql_log_bin=0; stop slave; reset slave all; CHANGE MASTER TO MASTER_HOST='10.0.0.201', MASTER_PORT=3306, MASTER_USER='repl', MASTER_PASSWORD='mysql', MASTER_LOG_FILE='binlog.000017', MASTER_LOG_POS=155; start slave; set sql_log_bin=1; stop slave sql_thread; start slave sql_thread;
復制相關系統變量
• server_id:是必須設置在master和每個slave上的唯一標識ID,其取值范圍是1~4294967295之間,且同一個復制組之內不能重復
• server_uuid:server_uuid會在GTID復制中使用。當MySQL啟動之后,會首先到數據文件目錄下的auto.cnf中尋找是否有指定的server_uuid,如果沒有找到,則自己生成一個server_uuid並保存到這個文件中
[auto]
server_uuid=8a94f357-aab4-11df-86ab-c80aa9429562
• log_slave_updates:該參數用來控制是否將收到的主庫的更新數據的語句也記錄在slave自己的bin log中。正常情況下是不需要記錄的,但如果是想創建級聯復制關系,比如A->B->C,這其中B既要作為A的從庫,也要作為C的主庫,則需要既開啟log-bin參數,也要開啟log_slave_updates參數
• relay-log:該參數用來指定relay-log文件的基礎名稱,默認的名稱為host_name-relay-bin.xxxx,其中的xxxx結尾是依次遞增的數字
• replicate-do-db:該參數用來指定需要復制的數據庫。
在基於語句復制的環境中,指定該參數之后,則slave的SQL thread進程只會應用在本數據庫下的對象相關的語句。如果有多個數據庫需要復制,則這個參數要使用多次。但如果是涉及到跨庫操作語句,則復制會丟失,比如:
replicate-do-db=sales USE prices; UPDATE sales.january SET amount=amount+1000;
在基於行復制的環境中,只要數據庫對象是指定的庫,則復制都能正常,比如上述update語句由於january表是屬於sales庫的,則slave會復制並應用,同樣下面的語句在基於行復制的環境中也不會執行:
USE sales; UPDATE prices.march SET amount=amount-25;
• 在slave的my.cnf上設置replicate-do-db=course,重啟mysql
vim /etc/my.cnf [mysqld] replicate-do-db=course
/etc/init.d/mysqld restart
• 查看從庫的狀態信息:
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: course
• 在主庫上執行:
mysql> use course; mysql> insert into temp values(5,'e'); mysql> use course2; mysql> insert into temp values(13,'dd');
• 在備庫上查看復制情況:
mysql> use course; mysql> select * from temp; ##數據有復制 +------+------+ | id | name | +------+------+ | 1 | a | | 2 | b | | 3 | c | | 4 | d | | 5 | e | +------+------+ mysql> use course2; mysql> select * from temp; ##數據沒有復制 +------+------+ | id | name | +------+------+ | 10 | aa | | 11 | bb | | 12 | cc |
• 在語句復制環境下查看對指定數據庫的修改操作:
vim /etc/my.cnf [mysqld] binlog_format=statement /etc/init.d/mysqld restart
mysql> show variables like '%binlog_format%'; +---------------+-----------+ | Variable_name | Value | +---------------+-----------+ | binlog_format | STATEMENT | +---------------+-----------+
• 主庫上執行:
• mysql> use test; • mysql> update test2.temp set name='ddd'; • mysql> use test2; • mysql> update test.temp set name='eee';
• 在從庫上查看復制結果:
• mysql> use test; • Reading table information for completion of table and column names • You can turn off this feature to get a quicker startup with -A • Database changed • mysql> select * from temp; ##雖然是指定的同步數據庫但並沒有同步 • +------+------+ • | id | name | • +------+------+ • | 1 | abc | • | 2 | abc | • | 3 | abc | • | 4 | abc | • | 5 | abc | • +------+------+ • mysql> use test2; • mysql> select * from temp; ##雖然不是指定的同步數據庫但數據有同步 • +------+------+ • | id | name | • +------+------+ • | 10 | ddd | • | 11 | ddd | • | 12 | ddd |
• 在行復制環境下查看對指定數據庫的修改操作:
mysql> show variables like '%binlog_format%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+
• 主庫上執行:
• mysql> use test; • mysql> update test2.temp set name='bcd'; • mysql> use test2; • mysql> update test.temp set name='abc';
• 在從庫上查看復制結果:
• mysql> use test; • mysql> select * from temp; ##數據已復制 • +------+------+ • | id | name | • +------+------+ • | 1 | abc | • | 2 | abc | • | 3 | abc | • | 4 | abc | • | 5 | abc | • +------+------+ • mysql> use test2; • mysql> select * from temp; ##數據未復制 • +------+------+ • | id | name | • +------+------+ • | 10 | aa | • | 11 | bb | • | 12 | cc | • +------+------+
另一個基於SQL語句復制和基於行復制的區別在於當語句中包含對多個數據庫的表進行操作時。比如設置replicate-do-db=db1,
USE db1; UPDATE db1.table1 SET col1 = 10, db2.table2 SET col2 = 20;
基於SQL語句的復制會將table1和table2都在備庫修改,而基於行的復制只會在備庫修改table1表
USE db4; UPDATE db1.table1 SET col1 = 10, db2.table2 SET col2 = 20;
而對於上述語句來說,基於SQL語句的復制不會在備庫修改任何表,而基於行的復制會在備庫修改table1表
如果希望跨庫的update語句在多個庫上都起作用,可以使用replicate-do-table=db_name.tbl_name
• replicate-ignore-db :該參數決定了忽略指定數據庫的復制,其行為和replicate-do-db正好相反 • replicate-do-table=db_name.tbl_name :通過該參數告知slave的SQL thread僅復制指定表上的數據。如果有多個表,則該參數要使用多次 • replicate-ignore-table=db_name.tbl_name :通過該參數告知slave的SQL thread將指定表上的數據過濾掉 • replicate-wild-do-table=db_name.tbl_name :通過該參數告知SQL的SQL thread僅復制符合匹配的表,可以使用_和%作為通配符。
比如replicate-wild-dotable=foo%.bar% 表示復制以foo打頭的數據庫下所有bar打頭的表數據。
如果是replicate-wild-do-table=foo%.% 表示即復制foo打頭的所有表的數據,也復制create/drop/alter database foo打頭的命令 • replicate-wild-ignore-table=db_name.tbl_name :通過該參數告知SQL的SQL thread過濾掉符合匹配的表
設置replicate-do-table參數,重啟mysql:
[mysqld] replicate-do-db=test replicate-do-table=test.temp
mysql> show slave status\G *************************** 1. row *************************** Slave_SQL_Running: Yes Replicate_Do_DB: test Replicate_Ignore_DB: Replicate_Do_Table: test.temp
在主庫上操作: mysql> use test; mysql> insert into temp values(6,'f'); mysql> insert into test2.temp values(14,'ff'); mysql> insert into temp2 values(4,'d'); 在從庫上查看復制情況:
在從庫上操作:
mysql> use test; mysql> select * from temp; ##數據被復制過來 +------+------+ | id | name | +------+------+ | 1 | abc | | 2 | abc | | 3 | abc | | 4 | abc | | 5 | abc | | 6 | f | mysql> select * from test2.temp; ##數據沒有復制 +------+------+ | id | name | +------+------+ | 10 | ddd | | 11 | ddd | | 12 | ddd | mysql> select * from temp2; ##數據沒有復制 +------+------+ | id | name | +------+------+ | 1 | a | | 2 | b | | 3 | c |
• slave-parallel-workers: 該參數決定了slave上啟動多個SQL thread線程來並行應用數據的。默認值是0代表不允許並行,取值范圍可以是0~1024
[mysqld] slave-parallel-workers=5
• skip-slave-start:該參數決定了在MySQL啟動時是否先不啟動slave線程,即暫停復制
[mysqld] skip-slave-start=1
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: Yes
vim /etc/my.cnf [mysqld] skip-slave-start=1
/etc/init.d/mysql.server restart mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: No Slave_SQL_Running: No mysql> start slave; mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: Yes
• slave-parallel-type=type:該參數決定了當啟動了並行之后,采用什么粒度的並行方式。默認值database表示按照不同的數據庫執行並行,LOGICAL_CLOCK則表示按照在binlog中的一組提交的事務作為並行粒度
mysql> show variables like 'slave_parallel_type'; +---------------------+----------+ | Variable_name | Value | +---------------------+----------+ | slave_parallel_type | DATABASE | +---------------------+----------+
• slave-skip-errors=[err_code1,err_code2,...|all|ddl_exist_errors]:該參數決定了當slave的SQL thread執行過程中碰到何種錯誤時可以忽略並繼續接下來的數據復制。正常情況下當有錯誤發生時,復制會停止而需要人工干預修復才能繼續進行。除非非常自信可以忽略某些錯誤,否則不要使用這個參數,不然會導致雖然復制執行正常,但其實內部的數據已經完全不一致
• sql_slave_skip_counter代表在非GTID復制環境下,通過設置此參數來跳過多少個復制事件。設置完該參數並非立即生效,而是要等待下次start slave命令的執行生效,並將該參數再次設置為0
• log-bin[=base_name] :該參數表示是否開啟binary log。默認情況下MySQL會使用host_name-bin.xxxx作為文件的名字,其中xxxx是以數字遞增的后綴。如果該參數指定了base_name,則二進制文件會以base_name.xxxx來命名
• binlog-do-db=db_name: 該參數決定了哪些庫下的修改會被記錄到bin log中。其行為與replicate-do-db類型,在基於SQL語句復制的環境下,只記錄在當前數據庫下的修改。比如指定binlog-do-db=sales,一下語句不會被記錄到bin log中:
USE prices;
UPDATE sales.january SET amount=amount+1000;
而以下語句則會被記錄到bin log中:
USE sales;
UPDATE prices.discounts SET percentage = percentage + 10;
而基於行復制的環境下,只有屬於指定數據的語句才會被記錄到bin log中。比如下面的語句會被記錄:
USE prices;
UPDATE sales.february SET amount=amount+100;
而下面的語句則不會被記錄:
USE sales;
UPDATE prices.march SET amount=amount-25;
針對跨庫的語句來說,行為和replicate-do-db相同
binlog-ignore-db=db_name:該參數決定了在bin log中忽略的數據庫,其行為與replicate-ignore-db類型
binlog_format:該參數決定了bin log中記錄的格式,可以是statement,row,mixed,分別代表基於SQL語句的復制,基於行復制和基於混合復制。在5.7.7版本之前的默認設置是statement,在5.7.7及以后,則默認是row。當設置為混合模式時,則優先使用statement,只有當基於語句的復制無法保證復制的准確時會自動替換為row
檢查復制狀態方法
通過在slave上執行show slave status來檢查復制是否正常工作
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event
Master_Log_File: binlog.000017 Read_Master_Log_Pos: 656 Slave_IO_Running: Yes Slave_SQL_Running: Yes Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Relay_Log_File: slave1-relay-bin.000006 Relay_Log_Pos: 319 Relay_Master_Log_File: binlog.000017 Exec_Master_Log_Pos: 656 Seconds_Behind_Master: 0
Slave_IO_State :代表當前slave的狀態
Slave_IO_Running :代表負責讀取主庫bin log的IO線程是否是運行狀態,正常情況下應該是YES
Slave_SQL_Running :代表負責執行備庫relay log的SQL線程是否是運行狀態,正常情況下應該是YES
Last_IO_Error, Last_SQL_Error :分別代表最后一次IO線程和SQL線程所發生的錯誤,正常情況下應該是空代表沒有錯誤
Seconds_Behind_Master :代表備庫的SQL線程比主庫的bin log晚多少秒。0代表目前沒有復制延遲
(Master_Log_file, Read_Master_Log_Pos) :表示IO線程在主庫bin log中的坐標位置
(Relay_Master_Log_File, Exec_Master_Log_Pos) :表示SQL線程在主庫bin log中的坐標位置
(Relay_Log_File, Relay_Log_Pos):表示SQL線程在備庫relay log中的坐標位置
在主庫可以通過執行show processlist命令查看主庫的bin log日志生成進程
mysql> show processlist\G *************************** 1. row *************************** Id: 4 User: event_scheduler Host: localhost db: NULL Command: Daemon Time: 79347 State: Waiting on empty queue Info: NULL *************************** 2. row *************************** Id: 20 User: repl Host: 10.0.0.204:50576 db: NULL Command: Binlog Dump Time: 11233 State: Master has sent all binlog to slave; waiting for more updates Info: NULL *************************** 3. row *************************** Id: 21 User: repl Host: 10.0.0.203:33874 db: NULL Command: Binlog Dump Time: 5530 State: Master has sent all binlog to slave; waiting for more updates Info: NULL
MySQL復制格式
• 基於語句復制的優勢:
屬於比較成熟的技術,得到廣泛使用當SQL語句會修改很多數據時,使用語句復制會比較節省空間由於二進制文件中包含了所有的修改語句,所以可以用來做審計功能
• 基於語句復制的劣勢:
某些特定的修改語句在基於語句復制的環境中復制會有問題,比如:語句中包含自定義函數或者不確定性的存儲過程update/delete語句中包含Limit語句但不包含order by語句屬於不確定性語句一些函數比如rand(), sysdate(),version()等由於不確定性也會導致復制異常每個導致復制異常的語句都會產生一個告警信息
[Warning] Statement is not safe to log in statement format.
• 基於行復制的優勢:
所有的數據庫修改都可以被復制,是一種安全的方式由於是行復制,所以某些語句在主庫和從庫上執行需要較少的lock
• 基於行復制的劣勢:
當DML語句涉及到多行的修改時,則由於行復制會把每行的修改信息都記錄下來,所以bin log會很大,有可能會導致復制的延遲相比較語句復制要大不能直接查看在備庫中執行的SQL語句建議僅使用InnoDB表做行復制,對MyISAM表的行復制有可能會導致復制異常
• 基於語句復制的二進制文件內容:
log-bin=mysql-bin server_id=1 binlog-format=statement 執行命令:/bin/mysqlbinlog data/mysql-bin.000002 # at 154 #170609 9:31:43 server id 1 end_log_pos 219 CRC32 0xe503603e Anonymous_GTID last_committed=0 sequence_number=1 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 219 #170609 9:31:43 server id 1 end_log_pos 302 CRC32 0x6c714c85 Query thread_id=4 exec_time=0 error_code=0 SET TIMESTAMP=1497000703/*!*/; SET @@session.pseudo_thread_id=4/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1342177280/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8 *//*!*/; SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; BEGIN /*!*/; # at 302 #170609 9:31:43 server id 1 end_log_pos 414 CRC32 0x2962b9af Query thread_id=4 exec_time=0 error_code=0 use `course`/*!*/; SET TIMESTAMP=1497000703/*!*/; insert into temp values('a','abc') /*!*/; # at 414 #170609 9:31:43 server id 1 end_log_pos 445 CRC32 0x82c53726 Xid = 55 COMMIT/*!*/
• 基於行復制的二進制文件內容:
vim /etc/my.cnf
log-bin=mysql-bin server_id=1 binlog-format=row
執行命令:bin/mysqlbinlog -v data/mysql-bin.000003
BEGIN /*!*/; # at 293 #170609 9:42:05 server id 1 end_log_pos 347 CRC32 0xa130fe8f Table_map: `course`.`temp` mapped to number 141 # at 347 #170609 9:42:05 server id 1 end_log_pos 389 CRC32 0x8deeb544 Write_rows: table id 141 flags: STMT_END_F BINLOG ' bW06WRMBAAAANgAAAFsBAAAAAI0AAAAAAAEABmNvdXJzZQAEdGVtcAACDw8ECgAKAAOP/jCh bW06WR4BAAAAKgAAAIUBAAAAAI0AAAAAAAEAAgAC//wBYgNiY2REte6N '/*!*/; ### INSERT INTO `course`.`temp` ### SET ### @1='b' ### @2='bcd' # at 389 #170609 9:42:05 server id 1 end_log_pos 420 CRC32 0x0bf24c6a Xid = 51 COMMIT/*!*/;
假如主庫的temp表數據展示如下:
mysql> select * from temp; +------+------+ | id | name | +------+------+ | 2 | eee | | 3 | eee | | 4 | eee | | 5 | eee |
備庫的temp數據展示如下:
mysql> select * from temp; +------+------+ | id | name | +------+------+ | 3 | eee | | 2 | eee | | 4 | eee | | 5 | eee |
主庫執行
delete from temp limit 1;
則主庫刪掉的數據是id=2的行,而備庫刪掉的是id=3的行。導致主備庫數據不一致
• 在statement環境下實驗主從一個表數據不一致的情況下復制是否還能繼續:
mysql> show variables like '%binlog_format%'; +---------------+-----------+ | Variable_name | Value | +---------------+-----------+ | binlog_format | STATEMENT | +---------------+-----------+
• 從庫執行: delete from temp where id<10000;
• 主庫執行: delete from temp where id=1;
• 查看從庫的同步狀態依然正常: mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: Yes Last_Errno: 0 Last_Error: Seconds_Behind_Master: 0
• 在行復制環境下實驗主從一個表數據不一致的情況下復制是否還能繼續:
mysql> show variables like '%binlog_format%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+ • 主庫執行:
delete from temp where id=2;
• 查看從庫的同步狀態不正常: mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: test Last_Errno: 1032 Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 4 failed executing transaction 'ANONYMOUS' at master log mysql-bin.000002, end_log_pos 386. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
• 如果手工在備庫增加這條數據,則同步再次正常: insert into temp values(2,’mike’); mysql> show slave status\G *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: Yes Last_Errno: 0 Last_Error: Skip_Counter: 0 Seconds_Behind_Master: 0
MySQL復制涉及三個線程,其中一個在主庫,另兩個在從庫
• binlog dump thread :在主庫創建,用來在從庫鏈接過來時發送bin log的內容
• slave io thread :在備庫創建,用來連接主庫並請求發送新的bin log內容。該線程讀取主庫的bin log dump線程發送的更新內容並將此內容復制到本地的relay log中
• slave sql thread :在備庫創建,讀取slave IO線程在本地relay log中的內容並在本地執行內容中的事件
MySQL復制線程
通過show processlist可以查看三個線程當前的狀態信息:
mysql> show processlist\G *************************** 2. row *************************** Id: 20 User: repl Host: 10.0.0.204:50576 db: NULL Command: Binlog Dump Time: 11233 State: Master has sent all binlog to slave; waiting for more updates Info: NULL *************************** 3. row *************************** Id: 21 User: repl Host: 10.0.0.203:33874 db: NULL Command: Binlog Dump Time: 5530 State: Master has sent all binlog to slave; waiting for more updates Info: NULL 在slave暫停復制的方法: 整體停止的方法:mysql> STOP SLAVE; 停止指定線程的方法: mysql> STOP SLAVE IO_THREAD; mysql> STOP SLAVE SQL_THREAD; 整體開啟的方法:mysql> START SLAVE; 開啟指定線程的方法: mysql> START SLAVE IO_THREAD; mysql> START SLAVE SQL_THREAD;
MySQL復制可以作為數據庫備份的一種解決方案,由於主庫的數據會復制到備庫,所以可以在備庫執行數據庫備份作業而不用影響主庫的性能
在備庫的備份通常有兩種選擇:
當數據庫比較小時,可以采用mysqldump的方式。由於mysqldump出來的文件內容是SQL語句,所以可以很方便的將其中的一部分復制出來應用到其他數據庫里。在執行mysqldump之前,為了保證數據的一致性,最好是把slave進程停掉。
mysqladmin stop-slave
或
mysql -e 'STOP SLAVE SQL_THREAD;’
mysqldump --all-databases > fulldb.dump mysqladmin start-slave
當數據庫比較大時,采用mysqldump方式的效率不高,所以可以使用物理文件拷貝的方式。為了保證數據的一致性,物理備份需要將備庫關閉
mysqladmin shutdown tar cf /tmp/dbbackup.tar ./data /etc/init.d/mysqld start
MySQL復制使用場景
MySQL復制可以用在主庫和從庫采用不同的存儲引擎的情況下。這樣做的目的通常是在主庫和從庫可以分別利用不同存儲引擎的優勢,比如在主庫使用InnoDB是為了事務功能,而從庫使用MyISAM因為是只讀操作而不需要事務功能
當使用mysqldump方式來創建備庫時,改變備庫的表存儲引擎的方式就是在應用dump文件之前先修改文件里的所有關於表存儲引擎的地方如果是使用文件拷貝的方式來創建備庫時,則唯一修改備庫表存儲引擎的方式就是在啟動備庫之后使用alter table命令修改
mysql> STOP SLAVE;
執行ALTER TABLE ... ENGINE=‘engine_type'命令
ALTER TABLE ... ENGINE=‘engine_type'
mysql> START SLAVE;
在從庫執行:
mysql> alter table temp engine='myisam';
在主庫執行:
mysql> insert into temp values(1,'a');
在從庫查看數據復制情況:
mysql> select * from temp; +------+------+ | id | name | +------+------+ | 3 | eee | | 4 | eee | | 5 | eee | | 6 | f | | 1 | a | | 2 | b |
MySQL復制可以用來做負載均衡功能的水平擴展,最主要是將數據庫的讀壓力分擔到多個MySQL slave實例上,這種情況適用在讀多寫少的環境中。比如一個基本的WEB架構:
MySQL復制可以用在當需要將主庫上的不同數據庫復制到不同的slave上,以便在不同的slave上執行不同的數據分析任務時。
可以在每個slave上配置不同的參數來約束復制過來的數據,通過replicate-wild-do-table參數或者replicate-do-db參數
slave1上應該配置參數replicate-wild-do-table=databaseA.%
slave2上應該配置參數replicate-wild-do-table=databaseB.%
slave3上應該配置參數replicate-wild-do-table=databaseC.%
每個slave其實是接收到完整的bin log日志,但在應用環節中會進行過濾,僅應用符合參數配置的事件在配置完參數之后,通過mysqldump的方式將對應數據庫在slave應用起來,再啟動slave線程
MySQL延遲復制
延遲復制是指定從庫對主庫的延遲至少是指定的這個間隔時間,默認是0秒。
可以通過change master to命令來指定
CHANGE MASTER TO MASTER_DELAY = N;
其原理是從庫收到主庫的bin log之后,不是立即執行,而是等待指定的秒數之后再執行
延遲復制的使用場景比如:
確保在主庫上被錯誤修改的數據能及時找回
測試在從庫IO集中在恢復bin log過程中對應用程序的訪問影響
保留一份若干天前的數據庫狀態,和當前狀態可以做對比
show slave status中SQL_Delay值表明了設置的延遲時長 mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_Running: Yes Slave_SQL_Running: Yes SQL_Delay: 60 在slave上執行: mysql> stop slave; mysql> CHANGE MASTER TO MASTER_DELAY = 60; mysql> start slave;
在主庫上執行: mysql> show master status; +------------------+----------+--------------+------------------+-------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------------------------------+ | mysql-bin.000003 | 3039 | | | 9eae8f34-47b6-11e7-8087-000c298d7ee3:1-23 | +------------------+----------+--------------+------------------+-------------------------------------------+ mysql> insert into temp values(4,'d');
mysql> show master status; +------------------+----------+--------------+------------------+-------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------------------------------+ | mysql-bin.000003 | 3318 | | | 9eae8f34-47b6-11e7-8087-000c298d7ee3:1-24 | +------------------+----------+--------------+------------------+-------------------------------------------+ 在slave上查看數據復制情況: mysql> select * from temp; ##數據沒有同步過來 +------+------+ | id | name | +------+------+ | 3 | eee | | 4 | eee | | 5 | eee | | 6 | f | | 1 | a | | 2 | b | | 3 | c | mysql> show slave status\G *************************** 1. row *************************** Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 3318 Relay_Log_File: vmware1-relay-bin.000002 Relay_Log_Pos: 693 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes Exec_Master_Log_Pos: 3039 Seconds_Behind_Master: 0 SQL_Delay: 60 SQL_Remaining_Delay: 6 Slave_SQL_Running_State: Waiting until MASTER_DELAY seconds after master executed event 在slave上查看數據復制情況: 等待60秒后數據同步過來 mysql> select * from temp; +------+------+ | id | name | +------+------+ | 3 | eee | | 4 | eee | | 5 | eee | | 6 | f | | 1 | a | | 2 | b | | 3 | c | | 4 | d | mysql> show slave status\G *************************** 1. row *************************** Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 3318 Relay_Log_File: vmware1-relay-bin.000002 Relay_Log_Pos: 972 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes Exec_Master_Log_Pos: 3318 Seconds_Behind_Master: 0 SQL_Delay: 60 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
MySQL復制主從切換
如果是使用GTID的復制方式,可以使用mysqlfailover工具做主從復制狀態的監控和自動切換;如果是使用非GTID模式,則需要使用其他的方式做監控和切換
當新的master產生之后,需要通過在其他slave上執行change master to語句來對應到新的master上。slave不會檢查自己的數據庫和新的master上是否一致,而是直接獲取master上的二進制日志並繼續自己的復制功能
新當選master的實例需要運行在log_bin模式下
• 新的master上開啟log-bin=mysql-bin • Master上查看bin log信息 mysql> show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set| +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.0000 | 154 | | |
• 在slave上執行: mysql> reset slave all; Query OK, 0 rows affected (0. sec) mysql> stop slave; mysql> mysql> CHANGE MASTER TO -> MASTER_HOST='192.168.237.130', -> MASTER_PORT=3308, -> MASTER_USER='repl', -> MASTER_PASSWORD='mysql', -> MASTER_LOG_FILE='mysql-bin.0000', -> MASTER_LOG_POS=154; mysql> start slave;
MySQL半同步復制
默認創建的MySQL復制是異步的,意味着主庫將數據庫修改事件寫入到自己的bin log,而並不知道從庫是否獲取了這些事件並應用在自己身上。所以當主庫崩潰導致要主從切換時,有可能從庫上的數據不是最新的
從5.7版本開始MySQL通過擴展的方式支持了半同步復制
當主庫執行一個更新操作事務時,提交操作會被阻止直到至少有一個半同步的復制slave確認已經接收到本次更新操作,主庫的提交操作才會繼續半同步復制的slave發送確認消息只會在本次更新操作記錄已經記錄到本地的relay log之后
如果沒有任何slave發送確認消息而導致超時時,半同步復制會轉換成異步復制
半同步復制會對MySQL性能產生影響,因為主庫的提交動作只有在收到至少一個從庫的確認消息之后才能執行。但這個功能是性能和數據可靠性方面的權衡
rpl_semi_sync_master_wait_point 參數用來控制半同步復制的行為:
AFTER_SYNC:默認值
AFTER_COMMIT
需要配置的系統參數包括:
rpl_semi_sync_master_enabled :在主庫配置,確保主庫的半同步復制功能開啟
rpl_semi_sync_master_timeout :配置主庫等待多少毫秒時間來保證接收備庫的確認消息,當超過這個時間時,半同步變成異步方式
rpl_semi_sync_slave_enabled :在從庫配置,確保從庫的半同步復制功能開啟
半同步復制是通過插件的方式建立,要分別在主庫和從庫安裝一個插件
前提條件: 5.5版本及以上 have_dynamic_loading參數必須是YES代表可以安裝插件並動態加載 事先建立好異步復制關系相關的插件安裝文件會在plugin_dir文件夾下,並以semisync_master和semisync_slave名字打頭
主庫上安裝插件: INSTALL PLUGIN rpl_semi_sync_master SONAME ‘semisync_master.so';
在每個從庫上安裝插件: INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
查看插件的安裝情況: mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE '%semi%'; +----------------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +----------------------+---------------+ | rpl_semi_sync_master | ACTIVE | +----------------------+---------------+
在主庫上開啟半同步復制: SET GLOBAL rpl_semi_sync_master_enabled = 1; SET GLOBAL rpl_semi_sync_master_timeout = N; ##N是毫秒,默認是10000,代表10秒
在備庫上開啟半同步復制: SET GLOBAL rpl_semi_sync_slave_enabled =1;
在備庫上重啟slave進程: STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
半同步復制監控參數:
Rpl_semi_sync_master_clients : 檢查半同步的slave個數
Rpl_semi_sync_master_status : 1表示主庫的半同步功能開啟並且運行正常,0表示主庫的半同步功能關閉或者半同步復制已經變成了異步復制
Rpl_semi_sync_master_no_tx : 表示有多少提交沒有收到slave的確認消息
Rpl_semi_sync_master_yes_tx : 表示有多少個提交收到了slave的確認消息
Rpl_semi_sync_slave_status : 1表示備庫上slave功能開啟並且運行正常,0表示功能為開啟或者運行異常
通過命令mysql> SHOW STATUS LIKE 'Rpl_semi_sync%';查看各個參數的狀態
mysql> SHOW STATUS LIKE 'rpl_semi_sync%'; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 2 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 3 | | Rpl_semi_sync_master_no_times | 1 | | Rpl_semi_sync_master_no_tx | 2 | | Rpl_semi_sync_master_status | ON | | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 5232 | | Rpl_semi_sync_master_tx_wait_time | 10465 | | Rpl_semi_sync_master_tx_waits | 2 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 2 | +--------------------------------------------+-------+
• 從庫關閉IO線程
STOP SLAVE IO_THREAD;
• 主庫執行update數據操作,需要等待10秒才能返回
update temp2 set name='ddd' where id=12;
• 超時返回之后,從庫的半同步狀態變成OFF狀態
mysql> show status like '%Rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | OFF |
• 當從庫同步正常后,半同步狀態顯示正常
mysql> START SLAVE IO_THREAD; mysql> show status like '%Rpl_semi%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Rpl_semi_sync_slave_status | ON | +----------------------------+-------+
• 當有兩個從庫都開啟半同步復制時,停止其中一個的slave IO線程,再在主庫上執行插入,操作很快返回
insert into temp2 values(131,'a');
• 當把第二個從庫的slave IO線程關閉時,則主庫插入數據需要等待10秒才能返回
insert into temp2 values(132,‘a’); ##等待10秒
MySQL基於GTID的復制
GTID(global transaction identifiers)復制是完全基於事務的復制,即每個在主庫上執行的事務都會被分配一個唯一的全局ID並記錄和應用在從庫上這種復制方式簡化了建立slave和master/slave之間切換的工作,因為其完全不需要找當前執行的bin log和log中的位置完成切換
一個GTID是master上執行的任何commit事務所分配的全局唯一ID標示,其由兩部分組成
GTID = source_id:transaction_id
Source_id代表主庫的server_uuid,transaction_id代表事務按順序提交的ID,比如第一個提交則是1,第十個提交的事務就是10
GTID集合代表一組GTID
① 當一個事務在主庫提交時,該事務就被賦予了一個GTID,並記錄在主庫的binary log
② 主庫的binary log會被傳輸到從庫的relay log中,從庫讀取此GTID並生成gtid_next系統參數
③ 從庫驗證此GTID並沒有在自己的binary log中使用,則應用此事務在從庫上
MySQL5.6的GTID復制模式,slave必須開啟bin-log和log_slave_updates參數,否則啟動就報錯,因為需要在binlog找到同步復制的信息(UUID:事務號)
(注:開啟log_slave_updates參數,是把relay-log里的日志內容再記錄到slave本地的binlog里。)
但在MySQL5.7里,官方做了調整,用一張gtid_executed系統表記錄同步復制的信息(UUID:事務號),這樣就可以不用開啟log_slave_updates參數,減少了從庫的壓力
從MySQL5.7.4版本開始,GTID會存放在mysql系統庫的gtid_executed表中
CREATE TABLE gtid_executed (
source_uuid CHAR(36) NOT NULL,
interval_start BIGINT(20) NOT NULL,
interval_end BIGINT(20) NOT NULL,
PRIMARY KEY (source_uuid, interval_start) );
mysql> show master status; +---------------+----------+--------------+------------------+----------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +---------------+----------+--------------+------------------+----------------------------------------+ | binlog.000003 | 445 | | | c685a42d-77f8-11ea-87fb-000c2959176a:1 | +---------------+----------+--------------+------------------+----------------------------------------+
mysql> select * from mysql.gtid_executed; +--------------------------------------+----------------+--------------+ | source_uuid | interval_start | interval_end | +--------------------------------------+----------------+--------------+ | 9eae8f34-47b6-11e7-8087-000c298d7ee3 | 1 | 1 | | 9eae8f34-47b6-11e7-8087-000c298d7ee3 | 2 | 2 | ……… +--------------------------------------+----------------+--------------+
創建復制流程
假定兩個數據庫實例間的主從關系已經通過傳統模式創建好了
• 將主庫和從庫都設置為read only,確保兩者之間的數據都完全同步
mysql> SET @@global.read_only = ON;
• 關閉主庫和從庫
mysqladmin -uusername -p shutdown
• 設置主從庫GTID后啟動 並暫時關閉slave進程
[mysqld] gtid-mode=on enforce-gtid-consistency=on skip-slave-start=1
enforce-gtid-consistency參數是確保只有對gtid復制機制安全的語句才會被log
• 重新設置主從庫的復制關系
CHANGE MASTER TO MASTER_HOST = host, MASTER_PORT = port, MASTER_USER = user, MASTER_PASSWORD = password, MASTER_AUTO_POSITION = 1;
• 啟動slave進程
START SLAVE;
• 關閉主庫的read only模式
SET @@global.read_only = OFF;
Master上執行:
insert into temp values(3,'c');
Slave上執行:
mysql> select * from temp; +------+------+ | id | name | +------+------+ | 1 | a | | 2 | b | | 3 | c | mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.237.128 Master_User: repl Master_Port: 3308 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 414 Relay_Log_File: vmware1-relay-bin.000002 Relay_Log_Pos: 627 Relay_Master_Log_File: mysql-bin.000002 Slave_IO_Running: Yes Slave_SQL_Running: Yes Last_Errno: 0 Last_Error: Retrieved_Gtid_Set: 9eae8f34-47b6-11e7-8087-000c298d7ee3:1 Executed_Gtid_Set: 9eae8f34-47b6-11e7-8087-000c298d7ee3:1 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version:
使用GTID復制的限制條件:
由於GTID復制是依賴於事務的,所以MySQL的一些屬性不支持
當一個事務中既包含對InnoDB表的操作,也包含對非事務型存儲引擎表(MyISAM)的操作時,就會導致一個事務中可能會產生多個GTID的情況;或者是當master和slave的表使用的存儲引擎不一樣時,都會導致GTID復制功能不正常
create table…select語句在基於語句復制的環境中是不安全的,在基於行復制的環境中,此語句會被拆分成兩個事件,一是創建表,二是insert數據,在某些情況下這兩個事件會被分配相同的GTID,而導致insert數據的操作被忽略,所以GTID復制不支持create table … select語句
create/drop temporary table語句在GTID復制環境中不能放在事務中執行,只能單獨執行,並且autocommit要開啟
sql_slave_skip_counter語句是不支持的,如果想要跳過事務,可以使用gtid_executed變量
mysql> create table temp2 select * from temp; ERROR 1786 (HY000): Statement violates GTID consistency: CREATE TABLE ... SELECT.
create table temp2(id int,name varchar(10)) engine=myisam; start transaction; insert into temp2 select * from temp; commit; start transaction; update temp set name='abc'; mysql> insert into temp2 select * from temp; ERROR 1785 (HY000): Statement violates GTID consistency: Updates to nontransactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.