MySQL插入时死锁

死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,这些事务都将无法向前推进,这里主要介绍插入时发生死锁的一些情况

模拟插入时发生死锁

新建一张表

表结构

CREATE TABLE `test_test` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `version` smallint DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

1. 插入两条相同的数据

t1 t2 状态
begin; begin;
insert ignore test_test (id, version) values (5, 1); 成功
insert ignore test_test (id, version) values (5, 1); 锁等待状态

insert加的是隐式锁,隐式锁可以理解为没有锁
在t1插入记录时是不加锁的,这个时候事务t1还未提交的情况下,事务t2尝试插入的时候,发现有这条记录,t2尝试获取S锁,会判定记录上的事务id是否活跃,如果活跃的话,说明事务未结束,会帮t1把它的隐式锁提升为显式锁(X锁)

2. 批量插入顺序不一致的导致的死锁

t1 t2 状态
begin begin
insert ignore test_test (id, version) values (5, 1); 成功
insert ignore test_test (id, version) values (6, 2); 成功
insert ignore test_test (id, version) values (6, 2); t1尝试获取S锁,把t2的隐式锁提升为显示X锁,进入DB_LOCK_WAIT
insert ignore test_test (id, version) values (5, 1); t2尝试获取S锁,把t1的隐式锁升级为显示X锁,产生死锁

3. 三个 insert 一个回滚造成的死锁

t1 t2 t3 说明
begin begin begin
insert ignore test_test (id, version) values (5, 1); 成功
insert ignore test_test (id, version) values (5, 1); 把t1的隐私锁升级为X锁,t2进去S锁等待
insert ignore test_test (id, version) values (5, 1); t3进入S锁等待
rollback t1回滚后,释放X锁,t2、t3同时拿到了S锁
ok deadlock t2和t3都想拿到插入意向X锁,造成死锁条件

一个已提交但是未purge掉的记录会造成后续插入获取S共享锁,两个事务同时获取S锁,然后尝试获取插入意向锁,造成死锁

首先,为待插入的间隙加插入意向锁(Insert Intention Locks)。

  • 如果该间隙已存在 GAP 锁或 Next-Key 锁,则加锁失败并进入等待状态。
  • 否则,加锁成功,表示此间隙允许插入。

接下来,判断插入的记录是否具有唯一键。

  • 若没有唯一键,直接进行插入操作。

  • 若存在唯一键,进行唯一性约束检查。

    • 若不存在相同的键值,完成插入操作。

    • 若存在相同的键值:

      1. 判断该键值是否已被锁定。

        • 若未被锁定:

          1. 判断该记录是否被标记为删除。

            • 若未标记删除,报错 1062(duplicate key)。

            • 若标记为删除,事务可能正在进行但尚未提交,因此加 S 锁并等待。

        • 若已被锁定:

          1. 表示该记录正在处理(插入、删除或更新),且事务未提交,加 S 锁并等待。

对插入的记录加 X 记录锁。

你可能感兴趣的:(mysql,数据库,java)