mysql主從 之 binlog格式詳解

binlog文件記錄格式statement、row、rixed三種,5.7之前默認爲statement模式,到5.7開始默認爲row模式。


statement就是語句模式,binlog記錄對數據做變動的所有語句,要看binlog記錄詳細內容可以用mysqlbing查看,現在來對statement模式進行測試:


這裏事先創建一個t2表做測試

mysql> show create table t2\G

*************************** 1. row ***************************

       Table: t2

Create Table: CREATE TABLE `t2` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `name` varchar(100) DEFAULT NULL,

  `date_time` datetime DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4

1 row in set (0.00 sec)

mysql> select @@binlog_format;

+-----------------+

| @@binlog_format |

+-----------------+

| STATEMENT       

+-----------------+

mysql> insert into t2(name,date_time) values(uuid(),now());

Query OK, 1 row affected, 1 warning (0.00 sec)

mysql> select * from t2\G;

*************************** 1. row ***************************

       id: 1

     name: 7ba7e11e-f6eb-11e5-a293-000c29fa3584

date_time: 2016-03-31 10:51:35

1 row in set (0.00 sec)


現在來查看binlog日誌記錄

# at 6450

# at 6482

#160331 10:51:35 server id 249  end_log_pos 6482 CRC32 0xa3e29747       Intvar

SET INSERT_ID=1/*!*/;

#160331 10:51:35 server id 249  end_log_pos 6611 CRC32 0x9fc70343       Query   thread_id=14    exec_time=0     error_code=0

SET TIMESTAMP=1459392695/*!*/;

insert into t2(name,date_time) values(uuid(),now())

/*!*/;

# at 6611

#160331 10:51:35 server id 249  end_log_pos 6642 CRC32 0xdff2aeb8       Xid = 170

COMMIT/*!*/;

binlog日誌是有一個個的event組成,所以一條sql就會產生數行記錄數據,由上面紅色部分可以看到有個SET INSERT_ID=1和SET TIMESTAMP,經過對時間戳轉換正好是我們插入數據的時間,表結構中ID其實是自增的,在這可以看出binlog在執行語句之前對ID和now()函數的值進行了記錄保證從數據庫數據一致,然而並未記錄uuid()函數的值,這就會導致主從不一致。


row模式顧名思義就是對每個變更的行數據進行記錄,下面就對它進行測試:

mysql> insert into t2(name,date_time) values(uuid(),now());

Query OK, 1 row affected (0.00 sec)

mysql> select * from t2 order by id desc limit 1;

+----+--------------------------------------+---------------------+

| id | name                      | date_time        |

+----+--------------------------------------+---------------------+

|  7 | 00e3a5b8-f6ed-11e5-86fc-000c29fa3584 | 2016-03-31 11:02:28 |

+----+--------------------------------------+---------------------+

1 row in set (0.00 sec)


產看binlog日誌記錄,因爲是row模式記錄的是MySQL內部識別的編碼所以需要加-v參數轉換爲我們能認識的行數據

BEGIN

/*!*/;

# at 337

#160331 11:02:28 server id 249  end_log_pos 385 CRC32 0xff339393        Table_map: `mc`.`t2` mapped to number 109

# at 385

#160331 11:02:28 server id 249  end_log_pos 468 CRC32 0x1560b09c        Write_rows: table id 109 flags: STMT_END_F


BINLOG '

RJP8VhP5AAAAMAAAAIEBAAAAAG0AAAAAAAEAAm1jAAJ0MgADAw8SA5ABAAaTkzP/

RJP8Vh75AAAAUwAAANQBAAAAAG0AAAAAAAEAAgADB/gHAAAAJAAwMGUzYTViOC1mNmVkLTExZTUt

ODZmYy0wMDBjMjlmYTM1ODSZmP6wnJywYBU=

'/*!*/;

### INSERT INTO `mc`.`t2`

### SET

###   @1=7

###   @2='00e3a5b8-f6ed-11e5-86fc-000c29fa3584'

###   @3='2016-03-31 11:02:28'

# at 468

#160331 11:02:28 server id 249  end_log_pos 499 CRC32 0x17f01cac        Xid = 9

COMMIT/*!*/;


上面綠色部分就是MySQL記錄的行記錄變更的編碼,紅色部分就是轉化出來的數據,由紅色部分看出其實是按我們表結構中字段的順序定義爲了1、2、3的變量進行了直接賦值,這樣只要主從數據結構一樣就能保證對函數產生的數據的一致性。


插入記錄方式瞭解了,那現在來看下row模式的update、delete語句操作是怎樣記錄的。

mysql> select * from t2 where date_time='2016-03-31 11:09:11';

+----+--------------------------------------+---------------------+

| id | name                      | date_time        |

+----+--------------------------------------+---------------------+

|  8 | f0cac692-f6ed-11e5-86fc-000c29fa3584 | 2016-03-31 11:09:11 |

+----+--------------------------------------+---------------------+

1 row in set (0.00 sec)

mysql> update t2 set name='a' where date_time='2016-03-31 11:09:11';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


BEGIN

/*!*/;

# at 939

#160331 11:19:03 server id 249  end_log_pos 987 CRC32 0x7ad2c2f9        Table_map: `mc`.`t2` mapped to number 109

# at 987

#160331 11:19:03 server id 249  end_log_pos 1032 CRC32 0x30d090d0       Update_rows: table id 109 flags: STMT_END_F


BINLOG '

J5f8VhP5AAAAMAAAANsDAAAAAG0AAAAAAAEAAm1jAAJ0MgADAw8SA5ABAAb5wtJ6

J5f8Vh/5AAAALQAAAAgEAAAAAG0AAAAAAAEAAgADAQL+CAAAAP4BAGHQkNAw

'/*!*/;

### UPDATE `mc`.`t2`

### WHERE

###   @1=8

### SET

###   @2='a'

# at 1032

#160331 11:19:03 server id 249  end_log_pos 1063 CRC32 0x9f1f68e7       Xid = 17

COMMIT/*!*/;


綠色部分就是要在從數據庫執行的記錄,可以看出來是用的where @1=8,按字段順序@1爲我們的主鍵ID,但是我們不是用date_time做的條件嗎,不急........下面再看下

mysql> alter table t2 modify id int,drop primary key;

Query OK, 8 rows affected (0.06 sec)

Records: 8  Duplicates: 0  Warnings: 0

mysql> select * from t2 where date_time='2016-03-31 11:09:11';      

+------+------+---------------------+

| id   | name | date_time           |

+------+------+---------------------+

|    8 | a    | 2016-03-31 11:09:11 |

+------+------+---------------------+

1 row in set (0.00 sec)

mysql> update t2 set name='aaa' where date_time='2016-03-31 11:09:11';

Query OK, 1 row affected (0.00 sec)

Rows matched: 1  Changed: 1  Warnings: 0


BEGIN

/*!*/;

# at 1378

#160331 11:23:05 server id 249  end_log_pos 1426 CRC32 0x603c2455       Table_map: `mc`.`t2` mapped to number 110

# at 1426

#160331 11:23:05 server id 249  end_log_pos 1481 CRC32 0x5edd4f26       Update_rows: table id 110 flags: STMT_END_F


BINLOG '

GZj8VhP5AAAAMAAAAJIFAAAAAG4AAAAAAAEAAm1jAAJ0MgADAw8SA5ABAAdVJDxg

GZj8Vh/5AAAANwAAAMkFAAAAAG4AAAAAAAEAAgAD/wL4CAAAAAEAYZmY/rJL/gMAYWFhJk/dXg==

'/*!*/;

### UPDATE `mc`.`t2`

### WHERE

###   @1=8

###   @2='a'

###   @3='2016-03-31 11:09:11'

### SET

###   @2='aaa'

# at 1481

#160331 11:23:05 server id 249  end_log_pos 1512 CRC32 0x8f9b33cb       Xid = 20

COMMIT/*!*/;


這次我們把主鍵刪掉對記錄做的update操作,看出綠色部分where條件對我們所有字段都做了匹配的,delete操作也是一樣,由此可以看出在row模式下從服務器做數據更改時是根據主鍵對數據查找進行更新,如果沒有主鍵就是全部掃描。


現在來對最後一個mixed混合模式進行測試:

mysql> insert into t2(name,date_time) values(uuid(),now());

Query OK, 1 row affected (0.00 sec)

mysql> insert into t2(name,date_time) values('a',now());      

Query OK, 1 row affected (0.00 sec)

mysql> select * from t2 order by id desc limit 2;

+----+--------------------------------------+---------------------+

| id | name                      | date_time       |

+----+--------------------------------------+---------------------+

| 14 | a                        | 2016-03-31 11:31:11 |

| 13 | f4bb52a8-f6f0-11e5-a33c-000c29fa3584 | 2016-03-31 11:30:46 |

+----+--------------------------------------+---------------------+

2 rows in set (0.00 sec)


BEGIN

/*!*/;

# at 337

#160331 11:30:46 server id 249  end_log_pos 385 CRC32 0xfb29d460        Table_map: `mc`.`t2` mapped to number 109

# at 385

#160331 11:30:46 server id 249  end_log_pos 468 CRC32 0x93c87ab6        Write_rows: table id 109 flags: STMT_END_F


BINLOG '

5pn8VhP5AAAAMAAAAIEBAAAAAG0AAAAAAAEAAm1jAAJ0MgADAw8SA5ABAAZg1Cn7

5pn8Vh75AAAAUwAAANQBAAAAAG0AAAAAAAEAAgADB/gNAAAAJABmNGJiNTJhOC1mNmYwLTExZTUt

YTMzYy0wMDBjMjlmYTM1ODSZmP63rrZ6yJM=

'/*!*/;

### INSERT INTO `mc`.`t2`

### SET

###   @1=13

###   @2='f4bb52a8-f6f0-11e5-a33c-000c29fa3584'

###   @3='2016-03-31 11:30:46'

# at 468

#160331 11:30:46 server id 249  end_log_pos 499 CRC32 0xa8544a3c        Xid = 9

COMMIT/*!*/;

# at 499

#160331 11:31:11 server id 249  end_log_pos 564 CRC32 0x04029a22        GTID    last_committed=1        sequence_number=2

SET @@SESSION.GTID_NEXT= '81570ee3-e47e-11e5-a7cd-000c29fa3584:17'/*!*/;

# at 564

#160331 11:31:11 server id 249  end_log_pos 647 CRC32 0x0741bd22        Query   thread_id=2     exec_time=0     error_code=0

SET TIMESTAMP=1459395071/*!*/;

BEGIN

/*!*/;

# at 647

# at 679

#160331 11:31:11 server id 249  end_log_pos 679 CRC32 0xe4ca7495        Intvar

SET INSERT_ID=14/*!*/;

#160331 11:31:11 server id 249  end_log_pos 805 CRC32 0x01e84a88        Query   thread_id=2     exec_time=0     error_code=0

use `mc`/*!*/;

SET TIMESTAMP=1459395071/*!*/;

insert into t2(name,date_time) values('a',now())

/*!*/;

# at 805

#160331 11:31:11 server id 249  end_log_pos 836 CRC32 0xf9959f9c        Xid = 10

COMMIT/*!*/;


有表查詢結果限制我們第一次使用uuid插入時id值爲13,在binlog中可以看出是使用了row模式記錄,第二次未使用uuid函數時就直接記錄的語句,由此可以得出mixed模式是當語句中含有uuid這種不安全的函數時就會使用row模式記錄。


經過上面反覆的的測試可以得出三種模式各自的優缺點:

    statement

      優點:直接記錄操作的語句,binlog產生量小,易於網絡傳送

      缺點:對uuid這種不安全的函數產生的數據不能保證主從一致

    row

      優點:記錄每行的數據,在數據結構一致的情況下能很好的保證主從數據一致性

      缺點:因爲記錄每行數據,產生數據量相對來說比較大,增加網絡傳輸量(binlog_row_p_w_picpath=minimal 可以減少一部分日誌量,but..假如誤操作就無法反向恢復),每行的變更都會在從查找執行,增加從的執行壓力,假如表未設置主鍵情況不容樂觀

    mixed

      優點:根據情況自動判斷採用什麼模式,可以減少binlog日誌量

      缺點:增加一次判斷也就增加了主機的壓力,而且對不安全的因素不一定能完全正確


在隔離級別爲RC的情況下,採用statement格式不使用不安全的函數也有可能導致主從數據不一致,大家都知道MySQL binlog是按事物的commit順序記錄,在一個事物對數據進行更新,爲及時提交,另外一個事物插入滿足前一個事物更新的條件,因爲RC並沒有間隙鎖機制,就會引起從數據和主數據不一致。


上面很多數據不一致的觀點並未到從庫應徵是否不一致,這方面的文檔網上有很多都介紹過,本文旨在詳細理解binlog各種模式,爲什麼會使數據不一致,各自得優缺點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章