大家好,我是小林。
说个很早之前自己遇到过数据库死锁的问题。
有个业务主要逻辑就是新增订单、修改订单、查询订单等操作。然后因为订单是不能重复的,所以当时在新增订单的时候做了幂等性校验,做法就是在新增订单记录之前,先通过 select ... for update 语句查询订单是否存在,如果不存在才插入订单记录。
而正是因为这样的操作,当业务量很大的时候,就可能会出现死锁。
接下来跟大家聊下为什么会发生死锁,以及怎么避免死锁。
死锁的发生
本次案例使用存储引擎 Innodb,隔离级别不可重复读(RR)。
接下来,我用实战的方式来带大家看看死锁是怎么发生的。
我建了一张订单表,其中 id 字段为主键索引,order_no 字段普通索引,也就是非唯一索引:
CREATE TABLE `t_order` (
`id` int NOT NULL AUTO_INCREMENT,
`order_no` int DEFAULT NULL,
`create_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_order` (`order_no`) USING BTREE
) ENGINE=InnoDB ;
然后,先 t_order 表里现在已经有了 6 条记录:
假设这时有两个事务,一个事务要插入订单 1007 ,另外一个事务要插入订单 1008,因为需要对订单做幂等性校验,所以两个事务先要查询该订单是否存在,不存在才插入记录,过程如下: