线上死锁解决方法

📅 2026/7/18 17:06:42
线上死锁解决方法
线上数据库死锁完整排查 解决方案MySQL 为主通用一、先快速定位线上死锁紧急止损1. 立刻查看死锁日志MySQL-- 查看最近一次死锁详情事务、SQL、锁、等待关系 SHOW ENGINE INNODB STATUS;重点看LATEST DETECTED DEADLOCK区块两个事务分别执行的 SQL各自持有什么锁、等待什么锁事务隔离级别、索引命中情况2. 查看当前阻塞事务-- 查看活跃事务、锁等待 SELECT * FROM information_schema.innodb_trx; -- 查看锁等待关系 SELECT * FROM information_schema.innodb_locks; SELECT * FROM information_schema.innodb_lock_waits;3. 紧急恢复线上服务死锁会自动回滚代价更小的事务但高并发下会频繁触发报错临时缓解杀掉长时间阻塞事务KILL 事务ID;临时降并发、限流、关闭高频同步接口临时拆分大事务缩短执行时间二、死锁根本成因90% 线上问题根源InnoDB 行锁死锁四要素互斥、持有并等待、不可剥夺、循环等待高频场景多事务更新行顺序不一致最常见 T1update user set xx where id1 → id2 T2update user set xx where id2 → id1 互相持有对方需要的行锁循环等待死锁无索引 / 索引失效行锁升级为表锁大量锁冲突大事务一次更新几十上百行持有锁时间过长隔离级别REPEATABLE READ默认存在 Gap 锁 / Next-key 锁间隙锁冲突同一事务多次更新同一张表不同行、批量更新乱序读写并发SELECT ... FOR UPDATE 悲观锁争抢三、线上根治方案按优先级落地方案 1统一更新数据顺序最有效零成本所有业务更新多行时强制按主键 / 唯一索引升序排序杜绝循环等待。 错误示例// 随机顺序更新极易死锁 for(Long id : randomIds){ updateById(id); }正确写法// 统一升序不会出现循环等待 ids.sort(Long::compareTo); for(Long id : ids){ updateById(id); }批量 SQL 统一排序-- 批量更新前先排序ID UPDATE t SET status1 WHERE id IN (1,3,5) ORDER BY id;方案 2缩小事务粒度缩短锁持有时间事务只放 DB 操作移除 RPC、文件 IO、复杂计算大事务拆分批量更新拆成分批小事务中间提交释放锁避免长事务查询、业务逻辑移出事务禁止事务内 sleep方案 3保证更新条件命中索引避免表锁更新 / 悲观查询必须走唯一索引 / 主键否则 InnoDB 会锁全表并发必死锁。-- 无索引行锁变表锁并发大量冲突 UPDATE order SET status2 WHERE phone13800000000; -- 优化phone加唯一索引精准行锁排查索引失效EXPLAIN update xxx看 type 是否为 ALL全表扫描。方案 4规避 Gap 间隙锁冲突RR 隔离级能改用 RC 读已提交隔离级优先无 Gap 锁死锁大幅减少需确认业务无幻读要求transaction_isolation READ-COMMITTED查询更新使用精确等值主键不使用范围条件范围会加间隙锁-- 范围条件产生Gap锁容易死锁 UPDATE t SET num1 WHERE id100; -- 改为批量等值有序更新方案 5悲观锁优化减少锁争抢非强一致性场景用乐观锁替代SELECT FOR UPDATE增加 version 版本号UPDATE t SET numnum-1,versionversion1 WHERE id? AND version?;更新失败重试控制重试次数避免风暴必须悲观锁时统一加锁顺序、缩小锁定行数、尽早提交事务方案 6批量操作规范禁止一次性更新上千行分批提交每批 50~200 条批量 DELETE/UPDATE 强制 ORDER BY 主键排序批量插入用 INSERT ... ON DUPLICATE KEY避免先查后改多行争抢四、代码层面通用防死锁规范Java/MyBatis所有多 ID 更新、批量操作先排序主键再执行事务方法尽量短小禁止外部依赖耗时操作悲观锁只锁定必要数据不要大范围锁定乐观锁重试机制最多 3 次重试失败抛异常降级分布式场景分布式锁Redisson控制行操作串行避免跨实例死锁五、线上监控预警提前发现死锁MySQL 开启死锁日志持久化innodb_print_all_deadlocksON业务埋点捕获数据库死锁异常Deadlock found when trying to get lock告警推送监控指标死锁次数、锁等待时长、长事务数量、慢 SQL定时巡检SHOW ENGINE INNODB STATUS采集死锁日志入库分析六、死锁异常兜底处理程序降级捕获死锁异常后重试短重试间隔限制次数// 伪代码 int retry 0; while(retry 3){ try{ db.transaction(); break; }catch(DeadlockException e){ retry; Thread.sleep(10); } } if(retry 3) throw BusinessException(操作繁忙请重试);七、快速排查流程总结线上应急步骤业务报错出现死锁 → 执行SHOW ENGINE INNODB STATUS拿到死锁两条 SQL核对两条 SQL 更新 ID 顺序是否相反 → 统一排序修复EXPLAIN 检查是否无索引导致表锁 → 加索引查看事务是否过长 → 拆分大事务范围更新过多 Gap 锁 → 切换 RC 隔离或改用等值批量有序更新临时限流止损上线修复代码后观察死锁告警是否归零