mysql 之 主从binlog格式详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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_image=minimal 可以减少一部分日志量),每行的变更都会在从查找执行,增加从的执行压力,假如表未设置主键情况不容乐观

    mixed

      优点:根据情况自动判断采用什么模式,可以减少binlog日志量

      缺点:增加一次判断也就增加了主机的压力,而且对不安全的因素不一定能完全正确


在隔离级别为RC的情况下,采用statement格式不使用不安全的函数也有可能导致主从数据不一致,大家都知道MySQL binlog是按事物的commit顺序记录,在一个事物对数据进行更新,为及时提交,另外一个事物插入满足前一个事物更新的条件,因为RC并没有间隙锁机制,就会引起从数据和主数据不一致。

本文出自 “肖忠” 博客,请务必保留此出处http://xiaozhong991.blog.51cto.com/2354914/1758773

以上是关于mysql 之 主从binlog格式详解的主要内容,如果未能解决你的问题,请参考以下文章

MySQL主从配置

MySQL主从同步原理

sync_binlo主从复制

mysql主从日志的定期清理

mysql事务表和非事务表在binlog日志的不同处理

MySQL binlog 远程备份方法详解