25

假设:

  • 我正在使用 REPEATABLE_READ 或 SERIALIZABLE 事务隔离(每次访问一行时都会保留锁)
  • 我们正在谈论同时访问多个表的多个线程。

我有以下问题:

  1. 操作是否可能INSERT导致死锁?如果是这样,请提供一个详细的场景来说明死锁是如何发生的(例如,线程 1 执行此操作,线程 2 执行此操作,...,死锁)。
  2. 对于加分:对所有其他操作(例如 SELECT、UPDATE、DELETE)回答相同的问题。

更新: 3. 对于超级奖励积分:在以下情况下如何避免死锁?

给定表格:

  • 权限[id BIGINT PRIMARY KEY]
  • 公司[id BIGINT PRIMARY KEY, name VARCHAR(30), permission_id BIGINT NOT NULL, FOREIGN KEY (permission_id) REFERENCES permissions(id))

我创建一个新公司如下:

  • 插入权限;-- 插入 permissions.id = 100
  • 插入公司(名称,permission_id)值('Nintendo',100);-- 插入 company.id = 200

我删除公司如下:

  • SELECT permission_id FROM Companies WHERE id = 200; -- 返回permission_id = 100
  • 从 id = 200 的公司中删除;
  • DELETE FROM 权限 WHERE id = 100;

在上面的示例中,INSERT 锁定顺序是 [permissions, Companies],而 DELETE 锁定顺序是 [companies, permissions]。有没有办法解决这个例子REPEATABLE_READSERIALIZABLE隔离?

4

3 回答 3

33

通常,所有修改都可能导致死锁,而选择不会(稍后再讨论)。所以

  1. 不,你不能忽略这些。
  2. 您可以根据您的数据库和设置忽略选择,但其他选项会给您带来死锁。

您甚至不需要多个表。

创建死锁的最好方法是以不同的顺序做同样的事情。

SQL Server 示例:

create table A
(
    PK int primary key
)

第 1 节:

begin transaction
insert into A values(1)

第 2 节:

begin transaction    
insert into A values(7)

第 1 节:

delete from A where PK=7

第 2 节:

delete from A where PK=1

你会陷入僵局。所以证明插入和删除可能会死锁。

更新类似:

第 1 节:

begin transaction    
insert into A values(1)
insert into A values(2)
commit

begin transaction
update A set PK=7 where PK=1

第 2 节:

begin transaction
update A set pk=9 where pk=2    
update A set pk=8 where pk=1

第 1 节:

update A set pk=9 where pk=2

僵局!

SELECT 不应该死锁,但在某些数据库上它会死锁,因为它使用的锁会干扰一致性读取。不过,这只是糟糕的数据库引擎设计。

如果您使用 SNAPSHOT ISOLATION,SQL Server 将不会锁定 SELECT。Oracle & 我认为 Postgres 永远不会锁定 SELECT (除非你有 FOR UPDATE ,它显然是为更新保留的)。

所以基本上我认为你有一些不正确的假设。我想我已经证明了:

  1. 更新可能导致死锁
  2. 删除可能导致死锁
  3. 插入会导致死锁
  4. 您不需要超过一张桌子
  5. 确实需要不止一个会话

您只需要在 SELECT 上接受我的话;)但这取决于您的数据库和设置。

于 2013-05-14T03:49:55.893 回答
13

除了 LoztInSpace 的回答,即使没有或存在inserts也可能导致死锁。您所需要的只是一个唯一索引和一个相反的操作顺序。deletesupdates

Oracle 中的示例:

create table t1 (id number);
create unique index t1_pk on t1 (id);

--thread 1 :
insert into t1 values(1);
--thread 2
insert into t1 values(2);
--thread 1 :
insert into t1 values(2);
--thread 2
insert into t1 values(1);  -- deadlock !
于 2016-04-28T08:28:13.327 回答
0

让我们假设您有两个关系AB两个用户XY。表 A 被用户 X 写锁定,表 B 被 Y 写锁定。如果用户 X 和 Y 都使用下面的查询,那么下面的查询会给你一个死锁。

Select * from A,B

很明显,Select如果涉及多个表的连接操作是其中的一部分,则该操作可能会导致死锁。通常插入和删除操作涉及单个关系。所以他们可能不会造成死锁。

于 2013-05-14T02:58:01.930 回答