MySQL binlog的3种格式对比(statement,row,mixed)

写在前面

在这里插入图片描述
binlog主要用于主从复制和数据恢复,本文就一起来看下其都有哪些格式,以及这些模式都有哪些优点和缺点。

1:准备

1.1:打开binlog

my.ini
[mysqld]
...
log_bin=D:\\program_files\\phpstudy\\PHPTutorial\\MySQL\\binlog\\mysql-bin
server-id=1
#设置日志三种格式:STATEMENT、ROW、MIXED 。
binlog_format=ROW
...

查看:

mysql> show variables like '%binlog%';
+-----------------------------------------+----------------------+
| Variable_name                           | Value                |
+-----------------------------------------+----------------------+
| binlog_cache_size                       | 32768                |
| binlog_direct_non_transactional_updates | OFF                  |
| binlog_format                           | ROW                  |
| binlog_stmt_cache_size                  | 32768                |
| innodb_locks_unsafe_for_binlog          | OFF                  |
| max_binlog_cache_size                   | 18446744073709547520 |
| max_binlog_size                         | 1073741824           |
| max_binlog_stmt_cache_size              | 18446744073709547520 |
| sync_binlog                             | 0                    |
+-----------------------------------------+----------------------+
9 rows in set (0.04 sec)

1.2:环境

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.53-log |
+------------+
1 row in set (0.05 sec)

win10

1.3:测试数据

CREATE TABLE `binlog_demo` (
  `id` int(11) NOT NULL,
  `a` int(11) DEFAULT NULL,
  `t_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `a` (`a`),
  KEY `t_modified`(`t_modified`)
) ENGINE=InnoDB;

insert into binlog_demo values(1,1,'2020-07-01');
insert into binlog_demo values(2,2,'2020-07-02');
insert into binlog_demo values(3,3,'2020-07-02');
insert into binlog_demo values(4,4,'2020-07-04');
insert into binlog_demo values(5,5,'2020-07-05');

2:正戏

2.1:statement

注意数据恢复为初始状态!!!

  • 修改为statement
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> set session binlog_format='statement';
Query OK, 0 rows affected (0.04 sec)

mysql> show variables like 'binlog_format';
+---------------+-----------+
| Variable_name | Value     |
+---------------+-----------+
| binlog_format | STATEMENT |
+---------------+-----------+
1 row in set (0.00 sec)

mysql>
  • 查看当前binlog的状态
mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000002 |     5083 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

mysql> show binlog events in 'mysql-bin.000002';
+------------------+------+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Log_name         | Pos  | Event_type  | Server_id | End_log_pos | Info                                                                                                                                                                                                                                               |
+------------------+------+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| mysql-bin.000002 |    4 | Format_desc |         1 |         107 | Server ver: 5.5.53-log, Binlog ver: 4                                                                                                                                                                                                              |
...                                                                                                                                                         |
| mysql-bin.000002 | 5056 | Xid         |         1 |        5083 | COMMIT /* xid=58 */                                                                                                                                                                                                                                |
+------------------+------+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

这里需要记住5083这个日志位置,后续我们需要用到,另外xid用来关联redo log和binlog,在崩溃恢复时会使用到。

  • 执行一个delete操作
mysql> delete from binlog_demo where a>=4 and t_modified<='2020-07-10' limit 1;
Query OK, 1 row affected, 1 warning (0.07 sec)
  • 查看生成的binlog内容
C:\Users\Administrator>mysqlbinlog -vv D:\program_files\phpstudy\PHPTutorial\MySQL\binlog\mysql-bin.000002 --start-position=5083
...
delete from binlog_demo where a>=4 and t_modified<='2020-07-10' limit 1
/*!*/;
# at 5301
...

这里只保留了最关键的内容,可以看到记录的正是我们执行的sql语句,但是这样的记录在进行主从同步或者是数据恢复时可能会导致数据不一致的情况,为什么?其中一种可能原因是优化器在不同环境下选择了不同的索引,而不同的索引选择,则会导致limit 1的结果不同,即最终的结果就是删除了不同的数据,如下可以证明这种现象。

  • 准备环境和数据
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` varchar(36) DEFAULT NULL,
  `d` varchar(36) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

delimiter ;;
create procedure t_1003()
begin
  declare i int;
  set i=1;
  while(i<=100000) do
    insert into t values(i,substring(md5(rand()), 10, 30),substring(md5(rand()), 10, 30));
    set i=i+1;
  end while;
end;;
delimiter ;

begin;
call t_1003();
commit;

mysql> alter table t add index idx_c(`c`);
Query OK, 0 rows affected (0.54 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table t add index idx_d(`d`);
Query OK, 0 rows affected (0.62 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show index from t\G
*************************** 1. row ***************************
        Table: t
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 100282
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: t
   Non_unique: 1
     Key_name: idx_c
 Seq_in_index: 1
  Column_name: c
    Collation: A
  Cardinality: 200
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 3. row ***************************
        Table: t
   Non_unique: 1
     Key_name: idx_d
 Seq_in_index: 1
  Column_name: d
    Collation: A
  Cardinality: 200
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
3 rows in set (0.00 sec)
  • 测试使用不同索引时获取不同的结果
mysql> select id from t force index(idx_c) where c>'a' and d>'5' limit 1;
+------+
| id   |
+------+
| 4972 |
+------+
1 row in set (0.00 sec)

mysql> select id from t force index(idx_d) where c>'a' and d>'5' limit 1;
+-------+
| id    |
+-------+
| 60716 |
+-------+
1 row in set (0.00 sec)

binlog statement模式不仅仅有上述的问题,还有使用诸如now()等和当时环境有关的函数时,也会造成数据的不一致。

2.2:row

注意数据恢复为初始状态!!!

binlog_row_image参数需要设置为full,不然不会记录所有字段信息,只会记录能够表示一行的字段,如ID,从而导致无法使用其进行数据恢复:

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.24    |
+-----------+
1 row in set (0.01 sec)

mysql> show variables like '%binlog_row_image%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| binlog_row_image | FULL  |
+------------------+-------+
1 row in set (0.01 sec)
  • 修改为row
mysql> set session binlog_format='row';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)
  • 查看当前binlog的状态
mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000005 |     1779 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

这里需要记住1779这个日志位置,后续我们需要用到。

  • 执行一个delete操作
mysql> delete from binlog_demo where a>=4 and t_modified<='2020-07-10' limit 1;
Query OK, 1 row affected, 1 warning (0.07 sec)
  • 查看生成的binlog内容
C:\Users\Administrator>mysqlbinlog -vv D:\program_files\phpstudy\PHPTutorial\MySQL\binlog\mysql-bin.000005 --start-position=1779
...
### DELETE FROM `test`.`binlog_demo`
### WHERE
###   @1=4 /* INT meta=0 nullable=0 is_null=0 */
###   @2=4 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593792000 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
# at 1949
...
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

只保留了最关键的内容,可以看到此时记录的是可以具体定位到某行的sql信息,相当于如下的sql语句:

DELETE FROM `test`.`binlog_demo`
WHERE
  id=4
  a=4
  t_modified=1593792000

这样就避免了因为选错索引而导致删除不同数据的问题,也就是说row会将对应的语句翻译为对所有影响行的操作,如下测试影响多行的情况:

mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000005 |     1976 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

mysql> select id from binlog_demo where id<900;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  5 |
+----+
4 rows in set (0.00 sec)

mysql> update binlog_demo set a='aa' where id<900;
Query OK, 4 rows affected, 4 warnings (0.07 sec)
Rows matched: 4  Changed: 4  Warnings: 4

此时生成的binlog如下可以看到是针对多个行的update

C:\Users\Administrator>mysqlbinlog -vv D:\program_files\phpstudy\PHPTutorial\MySQL\binlog\mysql-bin.000005 --start-position=1976
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
...
'/*!*/;
### UPDATE `test`.`binlog_demo`
### WHERE
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2=1 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593532800 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### SET
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593532800 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### UPDATE `test`.`binlog_demo`
### WHERE
###   @1=2 /* INT meta=0 nullable=0 is_null=0 */
###   @2=2 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593619200 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593619200 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### UPDATE `test`.`binlog_demo`
### WHERE
###   @1=3 /* INT meta=0 nullable=0 is_null=0 */
###   @2=3 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593619200 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### SET
###   @1=3 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593619200 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### UPDATE `test`.`binlog_demo`
### WHERE
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=5 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593878400 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### SET
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593878400 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
...
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

那么针对使用了比如now()函数的更新会怎么记录呢?如下测试:

mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000007 |      318 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

mysql> update binlog_demo set a=9876 where id=5;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1  Changed: 1  Warnings: 0

查看binlog如下:

C:\Users\Administrator>mysqlbinlog -vv D:\program_files\phpstudy\PHPTutorial\MySQL\binlog\mysql-bin.000007 --start-position=318
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
...
'/*!*/;
### UPDATE `test`.`binlog_demo`
### WHERE
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1659417981 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### SET
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=9876 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1659417981 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
# at 494
...
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

可以看到将now()转成了:

在这里插入图片描述

不仅如此,还set了其他列的值。

2.3.1:恢复数据

由于row格式记录了删除数据完整信息,以及更新数据的更新前和更新后信息,那么当我们需要恢复数据时,完全也可以使用到这些信息,比如如下的binlog记录:

### DELETE FROM `test`.`binlog_demo`
### WHERE
###   @1=4 /* INT meta=0 nullable=0 is_null=0 */
###   @2=4 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1593792000 /* TIMESTAMP meta=0 nullable=0 is_null=0 */

如果是要恢复数据的话,就可以推导出对应的恢复数据sql语句是:

insert into `test`.`binlog_demo`(@1,@2,@3) values(4,4,1593792000);

再比如update对应的binlog:

### UPDATE `test`.`binlog_demo`
### WHERE
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=0 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1659417981 /* TIMESTAMP meta=0 nullable=0 is_null=0 */
### SET
###   @1=5 /* INT meta=0 nullable=0 is_null=0 */
###   @2=9876 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1659417981 /* TIMESTAMP meta=0 nullable=0 is_null=0 */

则对应的恢复数据的sql语句就是:

UPDATE `test`.`binlog_demo`
WHERE
  @1=5 
  @2=9876 
  @3=1659417981
SET
  @1=5
  @2=0
  @3=1659417981 

2.3:mixed

注意数据恢复为初始状态!!!

MySQL server层智能选择到底是使用statement,还是row,但是这种智能并不能保证百分百的正确,毕竟,在优化器智能的优化sql,选择合适的索引时,也会出现错选索引的情况,而选错索引,最多就是数据查询的慢一点,但是如果是智能选错了binlog的模式,可能就会出现数据一致性的问题,这个结果就比较严重了。所以,在生产环境,还是建议使用row。另外还有一个非常有力的理由让我们选择row(美团myflash工具就依赖于此),即:恢复数据!!!

写在后面

参考文章列表:

MySql Binlog statement row mixed 三种模式初探

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值