表结构

  1. CREATE TABLE `task` (
  2. `id` int NOT NULL AUTO_INCREMENT,
  3. `type` int NOT NULL,
  4. `biz_id` int NOT NULL,
  5. `name` varchar(32) DEFAULT NULL,
  6. PRIMARY KEY (`id`),
  7. UNIQUE KEY `uk_it` (`biz_id`,`type`)
  8. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

操作步骤

  1. // 关闭自动提交
  2. set autocommit = OFF;
  3. // 开始事务
  4. begin;
  5. // 插入表
  6. insert task(biz_id, type, name) values(123,1,'fk');
  7. // 再开两个会话窗口,执行同样的几步操作,效果如下:

session1插入成功:
image.png
session2插入阻塞:
image.png
session3插入阻塞:
image.png
然后session1回滚:
image.png
此时session2的表现:
image.png
此时session3的表现:
image.png
可以看到在session1回滚之后,session2成功了,session3死锁了。

  1. SS锁共享,XX锁互斥,XS锁互斥。
  2. 首先有这么一个事实,在有唯一索引的表进行insert,会先获取该行的S锁,用于判断是否存在唯一冲突,如果不存在那么继续获取X锁进行insert操作,如果存在那么那么由于XX互斥,所以获取X锁会阻塞。
  3. 但如果同样的情况,用的是insert into … on duplicate key update …,由于有on duplicate key兜底,所以不会先加S锁判断重复,而是直接加X锁。
  4. 基于这两点那么我们来看,首先为什么session1插入成功切未rollback之前,其他两个都阻塞,这比较好理解,session1能够正常获取到S锁判断无重复之后继续获取到X锁,而session2和3因为上述第二条描述导致阻塞。
  5. 在session1回滚之后,原X锁释放,此时session2和3都想拿到X锁,但两个各自都拿到了S锁,由于SX互斥,导致相互等待,需要其中一个死锁退出,保证另一个的执行。