减小事务粒度能缓解死锁,因其缩短锁持有时间、统一加锁顺序、缩小锁范围;需按主键升序分批处理、避免全表扫描、移出事务内耗时操作,并确保索引优化与执行计划合理。

为什么减小事务粒度能缓解 Deadlock found when trying to get lock
MySQL 的死锁不是“锁太多”,而是多个事务以不同顺序争抢同一组资源。事务越长,持有锁的时间越久,和其他事务交叉重叠的概率就越高。Deadlock found when trying to get lock 本质是 InnoDB 检测到循环等待并主动回滚了其中一个事务——这不是失败,是保护机制。但频繁出现,说明事务设计和执行路径存在竞争热点。
- 长事务里混着查询、更新、睡眠、外部 API 调用,锁会一直占着不放
- 批量更新
UPDATE ... WHERE id IN (...)如果传入无序 ID 列表,各事务加锁顺序可能不一致,直接诱发死锁 - 没加索引的
WHERE条件会导致全表扫描+间隙锁,锁范围远超预期
怎么安全地拆分一个大事务(以 UPDATE 场景为例)
不是简单把一个 UPDATE 拆成 10 个,而是要保证原子性边界清晰、锁范围可控、执行顺序可预测。
- 按主键升序处理:先
SELECT id FROM t WHERE ... ORDER BY id,再对结果集逐批UPDATE,确保所有事务加锁顺序一致 - 每批控制在 100–500 行以内;用
LIMIT+WHERE id > last_id游标式分页,避免OFFSET性能退化 - 显式加
FOR UPDATE时,只锁定真正要改的行;避免在事务开头查一堆数据却只改其中几行 - 如果业务允许,把非核心更新(如统计字段、日志记录)挪到事务外异步做
START TRANSACTION; UPDATE orders SET status = 'shipped' WHERE id IN (101, 102, 105) ORDER BY id; -- 不要写成 WHERE id IN (105, 101, 102),IN 子句本身不保证顺序,ORDER BY 才强制加锁顺序 COMMIT;
哪些操作看似“只读”,实则偷偷加锁引发死锁
很多人以为 SELECT 安全,但在 RR 隔离级别下,它可能触发间隙锁或临键锁,尤其配合唯一索引查找时。
解析 MySQL 8.0 的 DDL 脚本,并将表结构导出为 Excel 格式。适用于用户需将 MySQL 的 `CREATE TABLE` 语句转换为结构化的 Excel 文件场景,导出内容包含字段名、数据类型、长度及精度等信息。支持多张表的批量处理,每张表对应 Excel 中的一个独立工作表。适用于数据库文档编写、数据字典生成及模式分析等任务。
-
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE显式加锁,必须纳入事务粒度考量 -
SELECT带WHERE条件但没走索引 → 全表扫描 → 所有间隙都被锁住 → 极易和其他事务冲突 - 唯一索引等值查询(
WHERE sn = 'ABC')会加“next-key lock”,若该值不存在,就锁住对应间隙,别人插同范围数据就被堵住 - 用
EXPLAIN看执行计划,确认type是const或range,而不是ALL或index
事务内调用外部服务或复杂计算的风险
数据库锁不会因为你正在发 HTTP 请求或算 MD5 就自动释放。这段时间别的事务卡在等锁,超时后一起撞上死锁检测器。
- 把
curl_exec()、file_get_contents()、加密解密、模板渲染等全部移出事务块 - 如果必须依赖外部返回值才能更新,先查好必要数据、生成待更新内容,再进短事务执行
UPDATE - 应用层加分布式锁(如 Redis)只用于协调「谁来触发这次更新」,别让它替代数据库事务语义
- 监控
innodb_row_lock_time_avg和innodb_deadlocks状态变量,比单纯看错误日志更早发现问题
事务粒度不是越小越好,关键在“锁的持有时间”和“锁的覆盖范围”是否与业务真实需求匹配。最容易被忽略的是:你以为的“一行更新”,可能因为缺失索引或查询条件写法,实际锁住了整个索引段。










