Slave SQL_THREAD如何重放Relay log

複製的介紹: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 ~]# 
View Code

能夠看到,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+樹的索引組織表(IOT);
  • 每一個表都須要有一個彙集索引(clustered index);
  • 全部的行記錄都存儲在B+樹的葉子節點(leaf pages of the tree);
  • 基於彙集索引的增、刪、改、查的效率相對是最高的;
  • 若是咱們定義了主鍵(PRIMARY KEY),那麼InnoDB會選擇其做爲彙集索引;
  • 若是沒有顯式定義主鍵,則InnoDB會選擇第一個不包含有NULL值的惟一索引做爲主鍵索引;
  • 若是也沒有這樣的惟一索引,則InnoDB會選擇內置6字節長的ROWID做爲隱含的彙集索引(ROWID隨着行記錄的寫入而主鍵遞增,這個ROWID不像ORACLE的ROWID那樣可引用,是隱含的)。

綜上總結,若是InnoDB表的數據寫入順序能和B+樹索引的葉子節點順序一致的話,這時候存取效率是最高的,也就是下面這幾種狀況的存取效率最高:

  • 使用自增列(INT/BIGINT類型)作主鍵,這時候寫入順序是自增的,和B+數葉子節點分裂順序一致;
  • 該表不指定自增列作主鍵,同時也沒有能夠被選爲主鍵的惟一索引(上面的條件),這時候InnoDB會選擇內置的ROWID做爲主鍵,寫入順序和ROWID增加順序一致;
  • 除此之外,若是一個InnoDB表又沒有顯示主鍵,又有能夠被選擇爲主鍵的惟一索引,但該惟一索引可能不是遞增關係時(例如字符串、UUID、多字段聯合惟一索引的狀況),該表的存取效率就會比較差。

 

 

總結:

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

http://imysql.com/node/314

 

 

做者:陸炫志

出處:xuanzhi的博客 http://www.cnblogs.com/xuanzhi201111

您的支持是對博主最大的鼓勵,感謝您的認真閱讀。本文版權歸做者全部,歡迎轉載,但請保留該聲明。

相關文章
相關標籤/搜索