隔离级别可序列化的事务中的死锁

问题描述

我试图了解锁定如何与隔离级别一起工作。我经历了这个question,但听不懂打击作用

在这里,我要在不同的终端机中开始两项交易,并在其中读取同一行。当我尝试更新它们时,终端会一直等待更新。除此以外,没有其他查询在运行

这是我执行的一系列步骤

conn1: START TRANSACTION;
conn1: SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
conn2: START TRANSACTION;
conn2: SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
conn1: SELECT * from users WHERE id = 1;
conn2: SELECT * from users WHERE id = 1;
conn1: UPDATE users set name = 'name' WHERE id = 1; waiting...
conn2: UPDATE users set name = 'name' WHERE id = 1; waiting...

这是我的第一个问题
在这里,我想了解为什么两个连接都在等待,如果它们是谁有锁来更新行?

如果我将上述步骤更改为

conn1: START TRANSACTION;
conn1: SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
conn2: START TRANSACTION;
conn2: SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
conn1: UPDATE users set name = 'name' WHERE id = 1;
conn2: SELECT * from users WHERE id = 1; waiting...
conn1: commit
conn2: updated results

在这种情况下,不同之处在于我可以看到conn1具有锁定,直到提交或回滚更改为止,所有其他请求都将等待,并且如果conn1已提交,则将获得更新结果

这是我的第二个问题
如果我想锁定行并且如果我想让其他连接等待(甚至用于读取),直到该锁释放(提交或回滚),或者我应该使用for update子句,这是正确的方法吗?

数据库-Mysql 5.7

解决方法

SERIALIZABLE isolation level上的mysql文档说:

此级别类似于REPEATABLE READ,但是InnoDB隐式将所有普通的SELECT语句转换为SELECT ... LOCK IN SHARE MODE

关于自动提交的条款不适用于此处,因为您已明确启动事务。

这意味着在第一种情况下,两个事务都在同一记录上获得共享锁。然后,第一个事务(T1)尝试执行更新,这需要排他锁。由于T2拥有共享锁,因此无法授予该权限。然后T2尝试更新,但由于T1持有共享锁而无法更新。

是使用原子更新还是select ... for update语句来锁定记录,取决于您需要应用的应用程序逻辑。如果需要获取记录的数据并在更新记录之前进行一些复杂的计算,请使用select ... for update方法。否则,请进行原子更新。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...