複製的介紹:php
根據日誌定義的模式不同,能夠分爲:Statement(SBR)模式,Row(RBR)格式或者是MIXED格式,記錄最小的單位是一個Event,binlog日誌前4個字節是一個magic number,接下來19個字節記錄Format desc evnet:FDE。MySQL5.6版本增長了GTID複製。html
下面對三種binlog格式的優缺點:java
Statement(基於語句級的複製):node
優勢:python
1)Binlog文件較小mysql
2)日誌是包含用戶執行的原始SQL,方便統計和審計redis
3)出現最先binlog兼容較好sql
4)Binlog方便閱讀,方便故障修復安全
缺點:session
1)存在安全隱患,可能致使主從數據不一致
2)對一些系統函數不能準複製或是不能複製,例如
Load_file()
uuid()
User()
Found_rows()
Sysdate()
Row格式(記錄了行的修改狀況):
優勢:
1)相比Statement更安全的複製格式
2)在某些狀況下複製速度更快(SQL複雜,表有主鍵的時候)
3)系統的特殊函數也能夠複製
4)更少的鎖
缺點:
1)Binary log比較大 (mysql-5.6支持binlog_row_image)
2)單語句更新或者刪除表的行數過多,會造成大量binlog
3)沒法從Binlog看見用戶執行的SQL (mysql-5.6增長了一個新的Event: binlog_row_query_log_events,記錄用戶的query)
MIXED格式:
1)混合使用ROW和STATEMENT模式。對於DDL記錄會STATEMENT,
2)若是使用innodb表,事務級別使用了READ COMMITED 或READ UNCOMMITTED日誌級別只能使用Row格式
3)但在使用ROW格式中的DDL語句仍是會記錄來STATEMENT格式
4)MIXED模式,那麼在如下幾種狀況下會自動將binlog的模式,由SBR模式改爲RBR模式。
一、當DML語句更新一個NDB表時
二、當函數中包含UUID()時
三、2個及以上包含AUTO_INCREMENT字段的表被更新時
四、任何INSERT DELAYED語句時
五、用UDF時
六、視圖中必需要求使用RBR時,例如建立視圖是使用了UUID()函數
下面咱們看幅圖,再進行舉例子說明:(基於ROW格式的複製流程)
針對這個圖,咱們如下進行這四種測試:
1、表有主鍵索引
2、表有惟一鍵索引
3、表有普通索引
4、表沒有任何索引
測試一:表有主鍵
在Master上建立tb1表,有主鍵:
<master>(root@localhost) [xuanzhi]> SHOW variables like 'binlog_format'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+ 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> show create table tb1\G *************************** 1. row *************************** Table: tb1 Create Table: CREATE TABLE `tb1` ( `id` int(11) NOT NULL, `name` varchar(20) NOT NULL, `age` tinyint(4) NOT NULL, `sex` tinyint(4) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> select * from tb1; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | innodb | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | | 50 | codis | 30 | 1 | +----+--------+-----+-----+ 5 rows in set (0.00 sec) <master>(root@localhost) [xuanzhi]>
在slave上模擬數據不一致,把第一條數據修改下:
Database changed <slave>(root@localhost) [xuanzhi]> select * from tb1; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | innodb | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | | 50 | codis | 30 | 1 | +----+--------+-----+-----+ 5 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> update tb1 set name='innosql' where id=10; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]> select * from tb1; +----+---------+-----+-----+ | id | name | age | sex | +----+---------+-----+-----+ | 10 | innosql | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | | 50 | codis | 30 | 1 | +----+---------+-----+-----+ 5 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]>
這時主從數據已經不一致了,在master執行update操做會不會報1032沒有找到記錄的錯誤呢,咱們試試:
<master>(root@localhost) [xuanzhi]> update tb1 set name='oracle' where id=10; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]>
回Slave庫上查看同步是否有問題:
<slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | oracle | 20 | 0 | +----+--------+-----+-----+ 1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.10.132 Master_User: root Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000012 Read_Master_Log_Pos: 1632 Relay_Log_File: localhost-relay-bin.000005 Relay_Log_Pos: 1778 Relay_Master_Log_File: mysql-bin.000012 Slave_IO_Running: Yes Slave_SQL_Running: Yes
能夠看到數據是正常複製的,並且同步沒有斷開。
測試二:有惟一索引
查看錶結構,在id列上是有惟一索引的:
<master>(root@localhost) [xuanzhi]> show create table tb1\G *************************** 1. row *************************** Table: tb1 Create Table: CREATE TABLE `tb1` ( `id` int(11) NOT NULL, `name` varchar(20) NOT NULL, `age` tinyint(4) NOT NULL, `sex` tinyint(4) NOT NULL, UNIQUE KEY `id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]>
在從庫修改一行數據,模擬數據不一致:
<slave>(root@localhost) [xuanzhi]> select * from tb1; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | oracle | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | | 50 | codis | 30 | 1 | +----+--------+-----+-----+ 5 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> update tb1 set name='python' where id=10; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | python | 20 | 0 | +----+--------+-----+-----+ 1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]>
在Master進行update操做:
<master>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | oracle | 20 | 0 | +----+--------+-----+-----+ 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> update tb1 set name='java' where id=10; Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]>
回slave庫查看是否出現主從斷開的可能:
<slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+------+-----+-----+ | id | name | age | sex | +----+------+-----+-----+ | 10 | java | 20 | 0 | +----+------+-----+-----+ 1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.10.132 Master_User: root Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000012 Read_Master_Log_Pos: 2035 Relay_Log_File: localhost-relay-bin.000005 Relay_Log_Pos: 2181 Relay_Master_Log_File: mysql-bin.000012 Slave_IO_Running: Yes Slave_SQL_Running: Yes
能夠看到當主從數據不一到的時候,基於惟一鍵更新,同步也是正常的,數據也是同步過來的。
測試三:當表只有普通索引
在master上看到表只有普通索引:
<master>(root@localhost) [xuanzhi]> show create table tb1\G *************************** 1. row *************************** Table: tb1 Create Table: CREATE TABLE `tb1` ( `id` int(11) NOT NULL, `name` varchar(20) NOT NULL, `age` tinyint(4) NOT NULL, `sex` tinyint(4) NOT NULL, KEY `id` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]>
在slave庫上模擬數據不一致的狀況:
<slave>(root@localhost) [xuanzhi]> update tb1 set name='php' where id=10; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+------+-----+-----+ | id | name | age | sex | +----+------+-----+-----+ | 10 | php | 20 | 0 | +----+------+-----+-----+ 1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]>
回到master更新一條記錄看看:
<master>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+------+-----+-----+ | id | name | age | sex | +----+------+-----+-----+ | 10 | java | 20 | 0 | +----+------+-----+-----+ 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> update tb1 set name='nosql' where id=10; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]>
<slave>(root@localhost) [xuanzhi]> select * from tb1 where id=10; +----+------+-----+-----+ | id | name | age | sex | +----+------+-----+-----+ | 10 | php | 20 | 0 | +----+------+-----+-----+ 1 row in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.10.132 Master_User: root Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000012 Read_Master_Log_Pos: 2426 Relay_Log_File: localhost-relay-bin.000005 Relay_Log_Pos: 2369 Relay_Master_Log_File: mysql-bin.000012 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1032 Last_Error: Could not execute Update_rows event on table xuanzhi.tb1; Can't find record in 'tb1', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log mysql-bin.000012, end_log_pos 2399
咱們查看一下Binlog作了什麼操做:
[root@localhost ~]# mysqlbinlog -v --base64-output=DECODE-ROWS --start-position=2223 --stop-position=2399 /data/mysql/data/mysql-bin.000012 /*!40019 SET @@session.max_insert_delayed_threads=0*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 2223 #150626 9:12:20 server id 1 end_log_pos 2294 Query thread_id=14 exec_time=0 error_code=0 SET TIMESTAMP=1435281140/*!*/; SET @@session.pseudo_thread_id=14/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=0/*!*/; 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 2294 # at 2344 #150626 9:12:20 server id 1 end_log_pos 2344 Table_map: `xuanzhi`.`tb1` mapped to number 45 #150626 9:12:20 server id 1 end_log_pos 2399 Update_rows: table id 45 flags: STMT_END_F ### UPDATE xuanzhi.tb1 ### WHERE ### @1=10 ### @2='java' ### @3=20 ### @4=0 ### SET ### @1=10 ### @2='nosql' ### @3=20 ### @4=0 DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; [root@localhost ~]#
能夠看到,update語句執行的條件大體是這樣的:where id=xx and name=xx and age=xx and sex=xx,當這條語句在slave上被執行時,slave庫上發現id=10 name=java...這條記錄不存在,因此報了1032錯誤。(後來我看了有主鍵和惟一索引的binlog日誌信息,也是跟普通索引同樣的,只是有主鍵的和惟一索引的時候不會把這條記錄的數據都檢查一遍)
測試四:表沒有索引
在master查看錶結構,表是沒有索引的:
<master>(root@localhost) [xuanzhi]> show create table tb1\G *************************** 1. row *************************** Table: tb1 Create Table: CREATE TABLE `tb1` ( `id` int(11) NOT NULL, `name` varchar(20) NOT NULL, `age` tinyint(4) NOT NULL, `sex` tinyint(4) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec) <master>(root@localhost) [xuanzhi]> select * from tb1; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | nosql | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | +----+--------+-----+-----+ 4 rows in set (0.00 sec) <master>(root@localhost) [xuanzhi]>
在slave上修改數據,模擬主從數據不一致:
<slave>(root@localhost) [xuanzhi]> select * from tb1; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | nosql | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | +----+--------+-----+-----+ 4 rows in set (0.00 sec) <slave>(root@localhost) [xuanzhi]> update tb1 set name='zabbix' where id=10; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 <slave>(root@localhost) [xuanzhi]>
回到master庫執行update操做:
<master>(root@localhost) [xuanzhi]> update tb1 set name='imysql' where id=10; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0 <master>(root@localhost) [xuanzhi]> select * from tb1; +----+--------+-----+-----+ | id | name | age | sex | +----+--------+-----+-----+ | 10 | imysql | 20 | 0 | | 20 | myisam | 22 | 1 | | 30 | memory | 22 | 1 | | 40 | redis | 32 | 0 | +----+--------+-----+-----+ 4 rows in set (0.00 sec) <master>(root@localhost) [xuanzhi]>
回到slave查看同步狀況:
<slave>(root@localhost) [xuanzhi]> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.10.132 Master_User: root Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000012 Read_Master_Log_Pos: 2914 Relay_Log_File: localhost-relay-bin.000002 Relay_Log_Pos: 346 Relay_Master_Log_File: mysql-bin.000012 Slave_IO_Running: Yes Slave_SQL_Running: No Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 1032 Last_Error: Could not execute Update_rows event on table xuanzhi.tb1; Can't find record in 'tb1', Error_code: 1032; handler error HA_ERR_END_OF_FILE; the event's master log mysql-bin.000012, end_log_pos 2887
能夠看到,當沒有索引或者索引是普通索引的時候,Slave SQL_THREAD線程是全表掃描匹配記錄,當全表掃描匹配記錄時,Relay log在回放的時候發現沒有這記錄,因此同步斷開了。
咱們先了解下InnoDB引擎表的一些關鍵特徵:
綜上總結,若是InnoDB表的數據寫入順序能和B+樹索引的葉子節點順序一致的話,這時候存取效率是最高的,也就是下面這幾種狀況的存取效率最高:
總結:
1、 每種Binlog格式都有本身的優缺點,若是有數據複製的環境,建議Binlog使用ROW的格式
2、Innodb是索引組織表,建議在設置表的時候都儘管設置有主鍵,若是沒有主鍵,在插入大數據的時候,會致使嚴重的主從延時
3、若是表沒有主鍵索引或者惟一鍵索引,Slave回放Relay log時會全表掃描匹配記錄,這速度會很是慢。
參考資料:
http://17173ops.com/2014/09/14/mysql-faq-why-innodb-table-using-autoinc-int-as-pk.shtml?utm_source=tuicool
http://www.cnblogs.com/LMySQL/p/4514554.html
做者:陸炫志 出處:xuanzhi的博客 http://www.cnblogs.com/xuanzhi201111 您的支持是對博主最大的鼓勵,感謝您的認真閱讀。本文版權歸做者全部,歡迎轉載,但請保留該聲明。 |