1、事故现象
在一段事务内,执行到一个更新sql会阻塞(100%重现),导致接口超时/数据错误
sql:UPDATE user set money = money + 100 where user_id = 1
2、分析原因: 通过以下事务相关参数配置和日志分析
SHOW ENGINE INNODB STATUS ; // 它打印了很多关于 InnoDB 内部性能相关的计数器、统计、事务处理信息等,不过它显示的不是当前状态,而是过去某个时间范围内InnoDB存储引擎的状态
show FULL processlist; // 它可以查看当前 mysql 的一些运行情况,是否有压力,都在执行什么 sql,语句耗时几何,有没有慢 sql 在执行等等
SELECT * FROM information_schema.INNODB_TRX; SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
通过查询结果能看到sql阻塞时,有两个事务正在执行,一个持有S锁,一个等待获取X锁,所以阻塞的原因就是行级的X锁被S锁阻塞了(跟隔离级别有关),具体哪个地方触发的S锁未知
网上找到一个跟此次事故类似的mysql官方的的bug文档 文档地址: bugs.mysql.com/bug.php?id=… 大概原因就是: 死锁检测机制导致的误判
3、解决方式
update语句使用表连接的形式,避免行级锁升级为表级锁
UPDATE user t LEFT JOIN user p
ON t.user_id = p.user_id
SET t.money = (p.money+100) where t.user_id = 1