MySQL 5.7分区表一动就锁全表,因其分区为“逻辑分区”,Server层无法将分区裁剪结果下推至InnoDB锁管理模块,故所有DML/DDL均按整表加锁处理,不支持分区级锁定。

MySQL 5.7 的分区表加锁粒度大,不是“锁得狠”,而是根本没实现分区级锁——它对整个分区表统一加表级锁(LOCK TABLES 或隐式 MDL 锁),哪怕只查一个分区。
为什么 5.7 分区表一动就锁全表
5.7 的分区实现是“逻辑分区”:底层仍是 InnoDB 表,但 Server 层在解析 SQL 时无法将分区裁剪结果下推到存储引擎锁管理模块。所以:
- 任何
SELECT ... FOR UPDATE、UPDATE、DELETE涉及分区表,InnoDB 都收不到“只锁 p2”的指令,只能按传统方式对整张表加意向锁 + 行锁(或间隙锁) - 即使
WHERE条件能精准命中单个分区(如PARTITION (p2)),5.7 也不识别该 hint,照样走全表扫描+全表加锁路径 - DDL 操作(如
ALTER TABLE ... REORGANIZE PARTITION)更明显:5.7 必须重建整个表,全程持有MDL_EXCLUSIVE锁,业务写入完全阻塞
8.0 真正支持分区级锁定的关键改动
8.0 将分区信息深度集成进 InnoDB 存储引擎层,使锁管理器能感知分区边界。这不是配置开关,是架构级变更:
- Server 层完成分区裁剪后,会把目标分区 ID 显式传给 InnoDB;InnoDB 据此在对应分区的独立
B-tree根页上加锁,而非整表根页 -
SELECT ... PARTITION (p2) FOR UPDATE在 8.0 下只锁 p2 内的行,其他分区可并发读写 - 部分 DDL(如
ALTER TABLE ... DROP PARTITION、ADD PARTITION)变为元数据操作,不再重建数据,MDL锁持有时间从分钟级降到毫秒级 - 注意:
REORGANIZE/EXCHANGE仍需短暂全表锁,但范围仅限于涉及的分区,非全部
升级后验证分区锁是否生效
别只信文档,用实际行为验证:
- 开两个会话,分别执行:
SELECT * FROM orders PARTITION (p2) FOR UPDATE;和SELECT * FROM orders PARTITION (p3) FOR UPDATE;—— 两者应不互斥 - 查
information_schema.INNODB_TRX,看trx_mysql_thread_id对应的trx_operation_state是否为fetching rows而非starting index read(后者说明还在全表扫描) - 执行
SHOW ENGINE INNODB STATUS\G,在TRANSACTIONS部分检查HOLDING THE LOCK(S)显示的是否为类似RECORD LOCKS space id 123 page no 456 n bits 72—— 这个page no应属于目标分区的数据页,而非系统表空间页
容易被忽略的兼容性陷阱
分区锁变细了,但旧代码可能反而出问题:
- 原来靠“全表锁顺带保护其他分区”的隐式逻辑,在 8.0 下失效。例如:事务 A 更新 p1,事务 B 同时更新 p2,若业务逻辑要求 p1/p2 数据强一致,现在必须显式用
SELECT ... FOR UPDATE覆盖所有相关分区 - 5.7 中能跑通的
INSERT INTO t SELECT ... FROM t PARTITION (p1),在 8.0 可能触发ERROR 1785(不可重复读场景下的自引用限制),因分区级锁让检测更严格 - 监控脚本若依赖
INFORMATION_SCHEMA.PARTITIONS的TABLE_ROWS估算大小,要注意 8.0 的统计值默认延迟更新,需手动ANALYZE TABLE或调大innodb_stats_auto_recalc
分区锁粒度变小是好事,但它的价值只在“被正确使用”时才体现。升级后第一件事不是压测 QPS,而是翻出所有带 PARTITION 的 SQL,确认它们是否真的只访问预期分区,以及事务边界是否还兜得住业务一致性。


















