MySQL45讲之幻读

前言

本文介绍了什么是虚读,虚读存在的问题和解决方法,以及间隙锁带来的麻烦。

[En]

This paper introduces what is phantom reading, the existing problems and solutions of phantom reading, as well as the trouble caused by gap lock.

什么是幻读

什么是错觉有两个条件:

[En]

There are two conditions for what is illusion:

  1. 必须是”当前读”情况下才可能发生,”快照读”不会出现
  2. 只有插入操作才算幻读,更新和删除不算

幻读的问题

错觉带来的问题是什么?主要有两点:

[En]

What are the problems caused by illusions? There are two main points:

  1. 在同一个事务内执行的两次或多次”当前读”操作,结果不一致
  2. 数据和日志记录不一致

MySQL45讲之幻读

执行上述事务操作后,binlog 记录如下:

insert into t values(1,1,5); /*(1,1,5)*/
update t set c=5 where id=1; /*(1,5,5)*/

update t set d=100 where d=5;/*所有d=5的行,d改成100*/

update t set d=5 where id=0; /*(0,0,5)*/
update t set c=5 where id=0; /*(0,5,5)*/

执行的结果 id=1 这行是 (1,5,5),而 binlog 日志记录的是 (1,5,100),可见数据和日志是不一致的。

如何解决幻读

MySQL 在行锁(Record Lock)的基础之上,又引入了间隙锁(Gap Lock)。间隙锁就是对两条记录之间的间隙上锁,避免幻读在已经上锁情况下插入记录。 注意,间隙锁之间不是互斥的。

行锁和间隙锁的结合称之为 next-key 锁,是一个前开后闭的区间,比如 (-∞,0]、(0,5]、(5,10]、(10,+suprenum]。其中,suprenum 是 InnoDB 为每个索引添加的不存在的最大值。

间隙锁的困扰

间隙锁解决了幻读的问题,但也带来了事务并发度降低和死锁的问题。比如下面的业务逻辑:

begin;
select * from t where id=N for update;

/*如果行不存在*/
insert into t values(N,N,N);
/*如果行存在*/
update t set d=N set id=N;

commit;

为什么会出现僵局?请看下面的交易流程:

[En]

Why is there a deadlock? Look at the following transaction flow:

MySQL45讲之幻读

session A 和 session B 都对 (5,10) 间隙上锁,之后判断记录不存在,A 和 B 都进行记录插入操作,然后因为对方对 (5,10) 间隙上锁,导致锁等待,进入死锁状态。MySQL 检测到死锁后,重启 session A 事务,让 session B 先执行成功。

有没有一种简单的方法来避免这种僵局问题?

[En]

Is there an easy way to avoid this deadlock problem?

可以考虑将事务隔离级别设置为”提交读”,避免间隙锁同时提高事务的并发度。但是为了解决数据和日志的不一致问题,还需要将 binlog 格式设置为 row 模式。

参考

Original: https://www.cnblogs.com/flowers-bloom/p/mysql45-unreal-reading.html
Author: flowers-bloom
Title: MySQL45讲之幻读

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/508112/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球