背景
电商平台系统中,在秒级别并发下单时,需要去修改数据库表中的发货状态。这是就会有多个事务去请求修改。
报错信息
Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
报错.png
分析
从日志来看,修改状态这个oder_sn最终没有持久化到数据库中。说明该记录被回滚了。数据库层面的死锁。
我们发现死锁前的操作是想去修改order表这一条记录的状态,然后就死锁了。
因为这个时候是并发下单的。说明不止一个事务提交进来。可能事务A和事务B都想要去修改自己记录的状态。
如果 order_sn字段上没有索引,数据库为了找到需要更新的那一行,会进行全表扫描
在这个过程中,InnoDB 引擎会锁定所有它扫描过的行,即使这些行最终并不符合 WHERE条件(只是先锁住,再判断)。在高并发环境下,如果多个事务同时执行此更新操作,每个事务都可能锁住大量记录,极易形成循环等待,从而触发死锁
。MySQL 检测到死锁后,会选择回滚其中一个事务,导致看到的 Deadlock found when trying to get lock错误
解决
最方便的解决措施就是给表字段加索引。
也可以优化代码。事务尽可能小且执行速度快
ALTER TABLE static_ip_order ADD INDEX idx_order_sn (order_sn);