赞
踩
目录
innodb_locks_unsafe_for_binlog
脏读
一个事务读取到了其他事务中还没有提交的数据
不可重复读
不可重复读发生在更新的情景下。事务a读取数据,事务b更新数据并提交,事务a再次读取时读取到了b更新后的数据
幻读
幻读发生在插入的情景下。事务a读取数据,事务b插入数据并提交,事务a再次读取时读取到了b插入的数据
在SQL:1992 standard事务隔离级别标准中包含4种隔离级别:
Serializable(序列化)
系统中所有的事务以串行地方式逐个执行,所以能避免所有数据不一致情况。
但是这种以排他方式来控制并发事务,串行化执行方式会导致事务排队,系统的并发量大幅下降,使用的时候要绝对慎重。
Repeatable read(可重复读)
一个事务一旦开始,事务过程中所读取的所有数据不允许被其他事务修改。 可重复读是mysql的默认隔离级别
Read Committed(已提交读)
一个事务能读取到其他事务提交过的数据。
一个事务在处理过程中如果重复读取某一个数据,而且这个数据恰好被其他事务修改并提交了, 那么当前重复读取数据的事务就会出现同一个数据前后不同的情况。
在这个隔离级别会发生“不可重复读”和”幻读“的场景。
Read Uncommitted(未提交读)
一个事务能读取到其他事务修改过,但是还没有提交的(Uncommitted)的数据。
数据被其他事务修改过,但还没有提交,就存在着回滚的可能性,这时候读取这些“未提交” 数据的情况就是“脏读”。
在这个隔离级别会发生“脏读”场景。
事务隔离级别 | 脏读 | 不可重复读 | 幻影读 |
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 不可能 |
序列化 | 不可能 | 不可能 | 不可能 |
注意:可重复读是不会发生幻读的,百度的绝大部分文章都写的不对。可以看下面的”rr模式是否会造成幻读?“那段
该参数已过期(Deprecated)。可能在未来的版本被删除,可以不用管这个参数。
innodb_locks_unsafe_for_binlog默认为OFF,表示gap lock打开。next-key是innodb用来控制gap之前的记录的锁,next-key是gap-key和record key的结合。
如果将innodb_locks_unsafe_for_binlog设置为ON,也就是关闭了gap lock,也就没有了next-key,这是相当于rc隔离级别。因为gap-lock被禁止,所以这个时候就会发生insert幻读的情况
mysql通过参数控制事务隔离级别
mysql5.7官方文档:
"transaction_isolation was added in MySQL 5.7.20 as a synonym for tx_isolation, which is now deprecated and is removed in MySQL 8.0. Applications should be adjusted to use transaction_isolation in preference to tx_isolation"
<5.7.19用tx_isolation,>=5.7.20用transaction_isolation,8.0及以后就不会有tx_isolation
transaction_isolation是动态参数,可以直接修改
set global transaction_isolation='READ COMMITTED' --更改全局事务隔离级别,对新会话有效,对已有回合无效
set transaction_isolation='READ COMMITTED' 和 set session transaction_isolation='READ COMMITTED'都是更改当前会话的隔离级别
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL { REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED | SERIALIZABLE };
例如:set session TRANSACTION ISOLATION LEVEL read committed;
set transaction xxx:设置下一个事务的隔离级别,不能在事务中使用
set session transaction xxx:设置当前会话下所有事务的隔离级别。跟set transaction_isolation和set session transaction_isolation类似
set global transaction xxx:设置新会话的隔离级别,对已连接的会话没有影响。跟set global transaction_isolation类似
- --set transaction不能在事务中执行,set session transaction可以,因为set session transaction才是设置当前会话下所有事务的语句,set transaction是下一个事务
- mysql> start transaction;
- Query OK, 0 rows affected (0.00 sec)
-
- mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
- ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress
- mysql> set session TRANSACTION ISOLATION LEVEL READ COMMITTED;
- Query OK, 0 rows affected (0.03 sec)
- mysql> set global TRANSACTION ISOLATION LEVEL READ COMMITTED;
- Query OK, 0 rows affected (0.00 sec)
- mysql> show variables like '%iso%';
- +-----------------------+-----------------+
- | Variable_name | Value |
- +-----------------------+-----------------+
- | transaction_isolation | REPEATABLE-READ |
- | tx_isolation | REPEATABLE-READ |
- +-----------------------+-----------------+
- --set global不会更改当前会话的事物隔离级别
- --开启新会话
- mysql> show variables like '%iso%';
- +-----------------------+----------------+
- | Variable_name | Value |
- +-----------------------+----------------+
- | transaction_isolation | READ-COMMITTED |
- | tx_isolation | READ-COMMITTED |
- +-----------------------+----------------+
-

- --set session TRANSACTION只会更改当前会话
- mysql> set session TRANSACTION ISOLATION LEVEL repeatable read;
- Query OK, 0 rows affected (0.00 sec)
-
- mysql> show global variables like '%iso%';
- +-----------------------+----------------+
- | Variable_name | Value |
- +-----------------------+----------------+
- | transaction_isolation | READ-COMMITTED |
- | tx_isolation | READ-COMMITTED |
- +-----------------------+----------------+
- 2 rows in set (0.01 sec)
-
- mysql> show session variables like '%iso%';
- +-----------------------+-----------------+
- | Variable_name | Value |
- +-----------------------+-----------------+
- | transaction_isolation | REPEATABLE-READ |
- | tx_isolation | REPEATABLE-READ |
- +-----------------------+-----------------+
- 2 rows in set (0.02 sec)

在解答上面的问题之前需要了解mysql的binlog模式。binlog有三种模式:statement、row、mixed。
mysql5.0之前只有statement一种模式,这种模式在rc隔离级别下可能会导致主备不一致(sql执行顺序在主备库上不一样)。所以mysql采用了rr级别为默认隔离级别。rr级别下会有间隙锁保证事务的先后顺序,既可以保证主备一致。
binlog为row模式现已经是mysql的默认模式,它是基于行改变的binlog。在row模式下,无论是rr还是rc都不会导致主备不一致。
所以rc隔离级别在row模式下是可以用的,而且也建议使用rc。
在rc隔离级别下,只支持binlog日志模式为row,就算是mixed,mysqlserver还是使用row来记录binlog。
回到问题,为什么mysql选择可重复读rr作为默认隔离级别?因为mysql.50之前binlog只有statement模式,statement模式下只有使用rr隔离级别才不会产生binlog记录事务顺序错乱。mysql后续的版本也没有改变默认隔离级别为rc
在rr隔离级别下,需要保证事务的快照读。快照读可以防止可重复读和幻读的发生。实现快照读就需要比rc更多的锁(gap lock间隙锁仅存在于rr模式下),死锁的概率要高很多。
比如显示事务a:select * from tab1 where col1>100,事务a在没有提交和回滚的情况下,其他事务不可以插入和更新tab1上的至少col1>100的数据。col1<100也是有可能被锁住的(可以看下下面的测试)。
在真实场景中mysql在rr模式下可能会造成2个条件不交叉的语句造成死锁的情况。
而rc级别不会有这样的情况,因为允许幻读,select肯定不会阻塞插入。
答案是不会。
“Repeatable read(可重复读) :一个事务一旦开始,事务过程中所读取的所有数据不允许被其他事务修改。
这个隔离级别没有办法解决“幻影读”的问题。
因为它只“保护”了它读取的数据不被修改,但是其他数据会被修改。如果其他数据被修改后恰好满足了 当前事务的过滤条件(where语句),那么就会发生“幻影读”的情况。”
上面这段话是摘自网络,百度随便一搜都是可重复读会发生幻读,或者这样:
但是无论是mysql官方文档还是我自己的测试,可重复读级别都是不会发生幻读的
mysql官网对可重复读级别的解释:
Consistent reads within the same transaction read the snapshot established by the first read
可重复读是在一个事务中第一次select时建立的快照读。也就是说在同一个事务中同样的select不会有其他不一样的结果,也就不会有幻读和不可重复读
mysql官网对幻读的解释:
https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html
mysql为了解决幻读使用了next-key lock,next-key lock是row-index lock(记录锁)和gap lock(间隙锁)的结合。
gap lock是mysql在rr级别特有的,rc级别不存在。也就是说mysql是通过lock机制解决rr上的幻读。既然rr级别mysql通过gap锁和next-key锁解决了幻读的发生,那么rr就不会有幻读
(测试均关闭自动提交)
幻读测试:
rr级别下幻读测试 | |
session1 | session2 |
mysql> select * from testa; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 1 | 1 | | 1 | 3 | 3 | | 1 | 3 | 3 | +------+------+------+ | mysql> select * from testa; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 1 | 1 | | 1 | 3 | 3 | | 1 | 3 | 3 | +------+------+------+ |
mysql> insert into testa values(1,1,1); mysql> commit; | |
mysql> select * from testa; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 1 | 1 | | 1 | 3 | 3 | | 1 | 3 | 3 | | 1 | 1 | 1 | +------+------+------+ 4 rows in set (0.00 sec) | |
mysql> select * from testa; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 1 | 1 | | 1 | 3 | 3 | | 1 | 3 | 3 | +------+------+------+ 3 rows in set (0.00 sec) | |
mysql> commit;--事务提交 mysql> select * from testa; | |
结论:rr级别下没有幻读 |
不可重复读测试:
rr级别下不可重复读 | |
session1 | session2 |
mysql> select * from testa where c=3; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 3 | 3 | | 1 | 3 | 3 | +------+------+------+ | mysql> select * from testa where c=3; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 3 | 3 | | 1 | 3 | 3 | +------+------+------+ |
mysql> update testa set a=2 where c=3; Query OK, 2 rows affected (0.03 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql> commit; mysql> select * from testa where c=3; | |
mysql> select * from testa where c=3; +------+------+------+ | a | b | c | +------+------+------+ | 1 | 3 | 3 | | 1 | 3 | 3 | --查看不到update后的数据 +------+------+------+ | |
mysql> commit;--事务提交 mysql> select * from testa where c=3; | |
rr级别没有不可重复读 |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。