MySQL半一致性读导致语句级Binlog复制错误1111

MySQL事务的隔离级别为read committed(或以下),或者设置了innodb_locks_unsafe_for_binlog参数会启用半一致性读特性。

半一致性读参考: http://www.linuxidc.com/Linux/2017-02/140844.htm

MySQL官方文档refman-5.6-en.a4.pdf 1833页 关于innodb_locks_unsafe_for_binlog参数

对于半一致性读,我感觉一个是违反两阶段锁,将不符合条件记录的行级锁提前释放。另一个是Update的行如果被锁定,则返回一个最近提交的版本。

官方文档原文如下:
Enabling innodb_locks_unsafe_for_binlog has additional effects:
? For UPDATE or DELETE statements, InnoDB holds locks only for rows that it updates or deletes.
Record locks for nonmatching rows are released after MySQL has evaluated the WHERE condition.
This greatly reduces the probability of deadlocks, but they can still happen.

? For UPDATE statements, if a row is already locked, InnoDB performs a “semi-consistent” read,
returning the latest committed version to MySQL so that MySQL can determine whether the row
matches the WHERE condition of the UPDATE. If the row matches (must be updated), MySQL reads
the row again and this time InnoDB either locks it or waits for a lock on it.

半一致性读本身是为了增加并发,但是对于STATEMENT格式的binlog则是致命的错误。
实验环境如下:
MySQL半一致性读导致语句级Binlog复制错误1111_第1张图片
初始化数据
CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB;
INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2);
COMMIT;

mysql> select * from t;
±–±-----+
| a | b |
±–±-----+
| 1 | 2 |
| 2 | 3 |
| 3 | 2 |
| 4 | 3 |
| 5 | 2 |
±–±-----+
5 rows in set (0.00 sec)

开启一个终端A,将b=3的记录修改为10
然后开启另外一个终端B,将b=2的记录修改为3并且提交,
最后提交终端A的事务。

MySQL半一致性读导致语句级Binlog复制错误1111_第2张图片

查看此时的结果
mysql> select * from t;
±–±-----+
| a | b |
±–±-----+
| 1 | 3 |
| 2 | 10 |
| 3 | 3 |
| 4 | 10 |
| 5 | 3 |
±–±-----+
5 rows in set (0.00 sec)

这个结果没有任何问题,但是查看binlog的内容
MySQL半一致性读导致语句级Binlog复制错误1111_第3张图片

备库执行后的数据则是
mysql> select * from t;
±–±-----+
| a | b |
±–±-----+
| 1 | 10 |
| 2 | 10 |
| 3 | 10 |
| 4 | 10 |
| 5 | 10 |
±–±-----+
5 rows in set (0.00 sec)

为了避免这个问题,可以将binlog_format设置为ROW。
MySQL binlog三种模式,设置模式为ROW
1.1 Row Level 行模式
日志中会记录每一行数据被修改的形式,然后在slave端再对相同的数据进行修改 优点:在row level模式下,bin-log中可以不记录执行的sql语句的上下文相关的信息,仅仅只需要记录那一条被修改。所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节。不会出现某些特定的情况下的存储过程或function,以及trigger的调用和触发无法被正确复制的问题 缺点:row level,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,会产生大量的日志内容。
1.2 Statement Level(默认)
每一条会修改数据的sql都会记录到master的bin-log中。slave在复制的时候sql进程会解析成和原来master端执行过的相同的sql来再次执行
优点:statement level下的优点首先就是解决了row level下的缺点,不需要记录每一行数据的变化,减少bin-log日志量,节约IO,提高性能,因为它只需要在Master上锁执行的语句的细节,以及执行语句的上下文的信息。
缺点:由于只记录语句,所以,在statement level下 已经发现了有不少情况会造成MySQL的复制出现问题,主要是修改数据的时候使用了某些定的函数或者功能的时候会出现。
1.3 Mixed 自动模式
在Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志格式,也就是在Statement和Row之间选择一种。如果sql语句确实就是update或者delete等修改数据的语句,那么还是会记录所有行的变更。
行模式和语句模式的区别
1.语句模式: 100万条记录 只需1条delete * from test;就可以删除100万条记录 2.row模式 100万条记录 记录100万条删除命令
1.4 企业场景如何选择binlog模式
1、互联网公司,使用MySQL的功能相对少(存储过程、触发器、函数) 选择默认的语句模式,Statement Level(默认) 2、公司如果用到使用MySQL的特殊功能(存储过程、触发器、函数) 则选择Mixed模式 3、公司如果用到使用MySQL的特殊功能(存储过程、触发器、函数)又希望数据最大化一直,此时最好选择Row level模式
1.5 如何配置binlog
在数据库中查看binlog模式
show global variables like “%binlog_format%”;
MySQL半一致性读导致语句级Binlog复制错误1111_第4张图片
默认是STATEMENT 现在我想修改成ROW模式 方法1: 修改 my.cnf (Mac 修改 /usr/local/mysql/my.cnf 无效,需要修改 /etc/my.cnf) 在[mysqld] 下面添加

binlog_format=ROW
log-bin=mysql-bin

然后重启 mysqld
MySQL半一致性读导致语句级Binlog复制错误1111_第5张图片
方法2: 无需重新在线修改命令
SET global binlog_format=‘ROW’;
重启 mysql 后失效

你可能感兴趣的:(DB)