隔离级别可序列化的事务中的死锁
Deadlock in transaction with isolation level serializable
我试图了解锁定如何与隔离级别一起工作。我已经完成了这个 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
正如 mysql 关于 SERIALIZABLE isolation level 的文档所说:
This level is like REPEATABLE READ, but InnoDB implicitly converts all plain SELECT statements to SELECT ... LOCK IN SHARE MODE
关于自动提交的条款在这里不适用,因为您明确地开始了一个事务。
这意味着在第一种情况下,两个事务都获得了同一记录上的共享锁。然后第一个事务 (T1) 尝试执行更新,这需要一个独占锁。这不能被授予,因为 T2 持有共享锁。然后 T2 尝试更新,但由于 T1 持有共享锁而无法更新。
是否使用原子更新或select ... for update
语句来锁定记录,取决于您需要应用的应用程序逻辑。如果您需要获取记录的数据并在更新记录之前对这些数据进行一些复杂的计算,请使用 select ... for update
方法。否则,进行原子更新。
我试图了解锁定如何与隔离级别一起工作。我已经完成了这个 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
正如 mysql 关于 SERIALIZABLE isolation level 的文档所说:
This level is like REPEATABLE READ, but InnoDB implicitly converts all plain SELECT statements to SELECT ... LOCK IN SHARE MODE
关于自动提交的条款在这里不适用,因为您明确地开始了一个事务。
这意味着在第一种情况下,两个事务都获得了同一记录上的共享锁。然后第一个事务 (T1) 尝试执行更新,这需要一个独占锁。这不能被授予,因为 T2 持有共享锁。然后 T2 尝试更新,但由于 T1 持有共享锁而无法更新。
是否使用原子更新或select ... for update
语句来锁定记录,取决于您需要应用的应用程序逻辑。如果您需要获取记录的数据并在更新记录之前对这些数据进行一些复杂的计算,请使用 select ... for update
方法。否则,进行原子更新。