INSERT操作会导致死锁吗?

Gil*_*ili 22 sql deadlock insert

假设:

  • 我正在使用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))

我按如下方式创建了一家新公司:

  • INSERT INTO权限; - 插入permissions.id = 100
  • INSERT INTO公司(name,permission_id)VALUES('Nintendo',100); - 插入companies.id = 200

我删除公司如下:

  • SELECT permission_id FROM公司WHERE id = 200; - 返回permission_id = 100
  • DELETE FROM公司WHERE id = 200;
  • DELETE FROM权限WHERE id = 100;

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

Loz*_*ace 29

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

  1. 不,你不能忽视这些.
  2. 根据您的数据库和设置,您可以稍微忽略select,但其他人会给您死锁.

你甚至不需要多个表.

创建死锁的最佳方法是以不同的顺序执行相同的操作.

SQL Server示例:

create table A
(
    PK int primary key
)
Run Code Online (Sandbox Code Playgroud)

第一节:

begin transaction
insert into A values(1)
Run Code Online (Sandbox Code Playgroud)

第二节:

begin transaction    
insert into A values(7)
Run Code Online (Sandbox Code Playgroud)

第一节:

delete from A where PK=7
Run Code Online (Sandbox Code Playgroud)

第二节:

delete from A where PK=1
Run Code Online (Sandbox Code Playgroud)

你会陷入僵局.因此,证明插入和删除可能会死锁.

更新类似:

第一节:

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

begin transaction
update A set PK=7 where PK=1
Run Code Online (Sandbox Code Playgroud)

第二节:

begin transaction
update A set pk=9 where pk=2    
update A set pk=8 where pk=1
Run Code Online (Sandbox Code Playgroud)

第一节:

update A set pk=9 where pk=2
Run Code Online (Sandbox Code Playgroud)

僵局!

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

如果使用SNAPSHOT ISOLATION,SQL Server将不会锁定SELECT.Oracle和我认为Postgres永远不会锁定SELECT(除非你有FOR UPDATE,无论如何都明确保留了更新).

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

  1. 更新可能会导致死锁
  2. 删除可能会导致死锁
  3. 插入可能会导致死锁
  4. 您不需要多个表
  5. 您确实需要多个会话

你只需要对SELECT;)进行说明,但这取决于你的数据库和设置.

  • 因此,推断您的答案:只要您在事务提交之前不访问插入的行, INSERT 就不会导致死锁。在 #3 的情况下,很难想象有人会查找已插入但未提交的权限行,因为唯一的列是自动递增的 id。那是对的吗? (2认同)

Gri*_*aub 6

除了LoztInSpace的答案之外,inserts即使没有deletes或updates存在也可能导致死锁.您所需要的只是一个独特的索引和一个颠倒的操作顺序.

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 !
Run Code Online (Sandbox Code Playgroud)

  • @Steve 这里的主要思想是,当两个进程试图将相同的值插入到唯一键列中时,其中一个进程将被锁定并等待另一个进程提交或回滚。如果你理解了这一点,上面的例子就很简单了。 (5认同)
  • 为什么这会导致死锁而不是约束冲突和线程 2 提交的回滚? (3认同)
  • @Steve 是的,它只与交易相关。一般来说,我认为启用自动提交时不会出现死锁。 (3认同)
  • 我认为最简单(但并非总是可能)的解决方案是在插入之前对行进行排序. (2认同)
  • @KarlIvarDahl将约束违规抛出到thread-2是不正确的,因为thread-1还没有提交,因此没有违规. (2认同)