mysql中的跨表事务锁定与性能分析

来源:这里教程网 时间:2026-02-28 20:48:53 作者:

跨表 UPDATE 或 DELETE 事务会锁哪些行?

MySQL 的 InnoDB 在跨表事务中不是“锁整张表”,而是按实际访问的索引记录加行级锁(

RECORD LOCK
),但前提是你用的是可重复读(
REPEATABLE READ
)隔离级别,且语句能走索引。如果
UPDATE t1 JOIN t2 ON ...
中任意一张表扫描了全表(比如缺少关联字段索引),就可能升级为间隙锁(
GAP LOCK
)甚至临键锁(
NEXT-KEY LOCK
),导致意外阻塞。

实操建议:

检查执行计划:
EXPLAIN FORMAT=TREE
看是否走了索引,特别注意
t2
表的
ON
条件列是否有索引
避免在事务里写
UPDATE t1, t2 SET ... WHERE t1.id = t2.t1_id AND t2.status = 'pending'
—— 若
t2.status
没索引,InnoDB 可能对全表 t2 加锁
跨表操作优先拆成单表语句 + 应用层控制,比一条 JOIN 更可控

SELECT ... FOR UPDATE 跨表时锁范围怎么算?

SELECT ... FOR UPDATE
在多表场景下只锁定
FROM
子句中**显式引用且实际读取到的行**,不会自动延伸到 JOIN 的另一张表——除非你把它也写进
FROM
或子查询中。很多人误以为
SELECT t1.* FROM t1 JOIN t2 ON t1.id = t2.t1_id FOR UPDATE
会锁 t2 的行,其实不会,t2 只是被用来过滤,不参与锁定。

常见错误现象:事务 A 执行该语句后,事务 B 仍能修改 t2 的对应行,导致 A 后续 UPDATE t1 时依据的 t2 数据已变。

实操建议:

真要锁 t2 的行,得显式写进
FROM
SELECT t1.*, t2.* FROM t1 JOIN t2 ON t1.id = t2.t1_id FOR UPDATE
若只关心 t1 数据一致性,但逻辑依赖 t2 的某个字段值,应在事务开始前用
SELECT ... LOCK IN SHARE MODE
先锁住 t2 的相关行
注意:MySQL 8.0.22+ 支持
FOR UPDATE OF t2
语法,可精确指定锁定哪张表

死锁日志里看到 “WAITING FOR THIS LOCK TO BE GRANTED” 却没报错?

这是典型的隐式锁等待,不是死锁,但容易被忽略。InnoDB 死锁检测只触发于循环等待(A 等 B、B 等 A),而跨表事务中更常见的是线性阻塞:事务 A 锁了 t1.id=100 和 t2.id=200,事务 B 先锁 t2.id=200 再试图锁 t1.id=100 —— B 会卡在第二步,但不会报死锁,只是无限等待(直到

innodb_lock_wait_timeout
超时,默认 50 秒)。

性能影响明显:QPS 下降、连接堆积、监控里

innodb_row_lock_waits
持续上升。

实操建议:

查实时锁等待:
SELECT * FROM performance_schema.data_locks;
结合
SELECT * FROM performance_schema.data_lock_waits;
降低风险:跨表更新尽量按固定顺序访问表(如总是先 t1 后 t2),避免不同事务反向加锁 应用层设置合理超时:
SET innodb_lock_wait_timeout = 10
(会话级),让失败更快暴露

批量跨表操作为什么越跑越慢?

不是因为数据量大,而是锁粒度随事务增长而恶化。一个事务里执行 1000 次

UPDATE t1 JOIN t2
,InnoDB 会累积持有所有涉及的行锁,直到 COMMIT。期间任何其他事务只要碰其中任意一行,就阻塞。更糟的是,长事务还会拖慢 purge 线程,导致
history_list_length
上升、MVCC 快照膨胀。

实操建议:

拆成小事务:每 100 行
COMMIT
一次,用
WHERE id BETWEEN ? AND ?
分片,别用
LIMIT
(易漏行)
避免在事务里做非 DB 操作(如 HTTP 请求、文件写入),否则锁持有时间不可控 确认 binlog 格式:
binlog_format = ROW
是必须的,否则跨表 DML 在 statement 模式下可能主从不一致或锁范围异常

最常被忽略的一点:跨表事务的锁行为高度依赖执行计划,而执行计划又受统计信息、索引选择、优化器开关(如

optimizer_switch='index_merge=on'
)影响。上线前务必在生产镜像环境用真实数据压测锁表现,不能只看开发库的小表结果。

相关推荐