ALTER TABLE在MySQL 8.0中仍会锁表,根本原因是元数据锁(MDL)被未提交事务或Sleep连接持有导致阻塞;需用INNODB_TRX查长事务、metadata_locks定位持锁线程,ALGORITHM=INPLACE和LOCK=NONE不保证无锁,pt-osc是更可控的在线变更方案。

ALTER TABLE 在 MySQL 8.0 中仍会导致锁表故障,根本不是“版本升级就自动无锁”,而是锁类型、触发条件和降级逻辑发生了变化——你看到的 Waiting for table metadata lock,90% 以上不是数据锁,是元数据锁(MDL)被卡住。
查 INFORMATION_SCHEMA.INNODB_TRX 找真正在阻塞的长事务
SHOW PROCESSLIST 只能看到连接状态,但真正 hold 住 MDL 的,往往是那些没提交的 SELECT ... FOR UPDATE、UPDATE 或甚至只是 SELECT 后忘了 COMMIT 的事务。
-
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60是唯一能揪出活跃长事务的语句 - 注意:
SHOW OPEN TABLES WHERE In_use > 0对 MDL 无效,别信它 - 如果查到
trx_state = RUNNING且trx_started很早,直接KILL对应的trx_mysql_thread_id
看 performance_schema.metadata_locks 判断谁在抢锁
MySQL 8.0+ 把 MDL 信息暴露给了 performance_schema,比老版本更准:
-
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'table_name' - 关键看
LOCK_STATUS = 'PENDING'的行:它正等着某个LOCK_STATUS = 'GRANTED'的锁释放 -
OWNER_THREAD_ID能关联到performance_schema.threads,反查是哪个应用连接在持锁
别轻信 ALGORITHM=INPLACE 和 LOCK=NONE
这两个参数不是开关,是“申请书”:MySQL 会检查是否满足全部前提,不满足就报错或静默降级(比如改 VARCHAR(255) → VARCHAR(1024) 在 8.0.12+ 仍可能重建)。
- 加字段必须同时满足:加在末尾 + 无
NOT NULL DEFAULT值 + 表引擎为 InnoDB - 改默认值(
ALTER TABLE ... ALTER COLUMN ... SET DEFAULT)是 INSTANT 的,但仅限 8.0.12+,且不能带NOT NULL强制约束 -
EXPLAIN FORMAT=JSON执行 DDL 前缀语句(如EXPLAIN ALTER TABLE t ADD COLUMN x INT)才能看到"alter_algorithm": "inplace"是否真生效
pt-online-schema-change 不是银弹,但最可控
它绕开 MDL 锁的核心逻辑是:不改原表,而用触发器同步变更。但代价明确:
- 原表必须有主键或唯一非空索引,否则增量同步无法定位行
- 已存在触发器的表不能用,会冲突
-
--dry-run --print必须跑一遍,确认生成的RENAME TABLE顺序和库名正确,否则切表失败直接丢数据 - 主从架构下禁止只在从库运行再手动切主从——
pt-osc不复制 DDL,主从结构立刻不一致
INSTANT DDL 只覆盖极小一部分操作(末尾加列、删列、改默认值),其余多数变更仍走 INPLACE 或 COPY;而线上真正的瓶颈,往往不是 DDL 本身多慢,而是它卡在等一个两小时前没提交的事务。锁表故障的本质,是业务逻辑和数据库机制的时间差没对齐。


















