这是个历史问题,主要就是主从复制的问题了,说到主从复制就肯定离不开bin log
binlog 的三种格式:
- Statement:记录修改的sql
- raw:记录每行实际数据的变更
- mixed:1,2的混合
mysql5.1之前binlog的记录方式只支持statement方式,而这种格式在读已提交的隔离级别下主从复制是有bug的,所以将可重复读作为默认隔离级别!
接下来要讲bug是怎么发生的:
- 首先通过命令,查询一下默认隔离级别。
show variables like 'transaction_isolation';
- 设置session隔离级别为“读提交”
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-
在读提交的隔离级别下,开启两个会话:按照下面的顺序执行命令,最后得到的数据库只有一条记录
-
mysql5.1之前binlog的记录方式只支持statement方式,用于恢复和主从复制。主库就是将每次数据库的sql(增删改)修改在提交以前以二进制编码的形式保存到日志文件中。那么根据上图展示的情况,在提交前才会写入日志文件,那么日志文件中的sql语句顺序就会是 先插后删,所以在从库中是没有数据的,而在主库中是有数据的(因为session1先开始的事务,会触发锁,导致session2中的事务在session1提交之后才能生效)。
-
如何解决?
(1) 隔离级别设置为可重复读,在该隔离级别下引入间隙锁。当session1执行delete语句时,会锁住间隙,那么session2执行的插入语句就会堵塞住!
(2) 将binlog的格式修改为row格式,此时就会是基于行的复制,自然就不会出现sql不一致的情况,但是该格式是在mysql5.1版本后开始引入。
所以,mysql选择了可重复读作为默认的隔离级别。
在实际项目中应该使用哪种隔离级别?
- 正常情况下应该使用读已提交,相对于可重复读的优点如下:
(1) 因为可重复读存在间隙锁,所以可重复读导致出现死锁的概率比读已提交大。
(2) 在可重复读隔离级别下,条件列未命中索引会触发锁表,而读已提交只是行锁 - 个别情况:例如对账单:
需要对照余额表和账单表数据时,希望读出来的余额不受最新操作影响,就要使用到可重复读的隔离级别了