不能“快速”改,除非已确认数据完全兼容、表足够小且不care静默截断或锁表;盲目执行alter table ... modify column大概率丢数据、锁表、索引失效,甚至卡住线上服务。

不能“快速”改,除非你已确认数据完全兼容、表足够小、且不care静默截断或锁表现象。盲目执行 ALTER TABLE ... MODIFY COLUMN,大概率丢数据、锁表、索引失效,甚至线上服务卡住。
查数据兼容性前别敲任何 ALTER
MODIFY 不是改个声明,它会重写字段存储——超长值被截、越界数归极值、emoji 变 ?,全都不报错(除非开了 STRICT_TRANS_TABLES)。必须先验证:
- 字符串长度:比如要改成
VARCHAR(50),先跑SELECT COUNT(*) FROM t WHERE LENGTH(c) > 50;结果非零?别硬上 - 数值范围:要缩成
TINYINT?先查SELECT MIN(c), MAX(c) FROM t,对照-128 ~ 127看是否越界 - 四字节字符:要从
utf8mb4改utf8?用SELECT c FROM t WHERE c REGEXP '[\xF0-\xF7][\x80-\xBF]{3}'扫一遍 emoji - 原字段定义:用
SHOW CREATE TABLE t抄全声明,漏掉NOT NULL或COMMENT,改完就变可空、注释消失
MODIFY 和 CHANGE 别混用,语法错一个字就失败
只改类型不改名,必须用 MODIFY COLUMN;要改名+改类型,才用 CHANGE COLUMN。它们不是可互换的快捷方式:
-
ALTER TABLE t MODIFY COLUMN c INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态'—— 所有约束都得重写,漏NOT NULL就变可空 -
ALTER TABLE t CHANGE COLUMN c c INT UNSIGNED NOT NULL DEFAULT 0—— 第二个c是强制重复,少写或写错(如写成d)就真改名了 -
ALTER TABLE t CHANGE COLUMN c INT UNSIGNED—— 漏第二个字段名,直接ERROR 1064 -
TEXT/BLOB字段不能带DEFAULT,语句里写了就ERROR 1101,删掉再试
大表改类型不等于加个 ALGORITHM=INPLACE 就安全
MySQL 声称支持在线 DDL,但以下情况仍会退化为 COPY 模式:字段长度缩窄、跨字符集(utf8mb4 → utf8)、TEXT 类型变更、ENUM 调序。后果是整表 SX 锁、磁盘爆满、DML 阻塞。
- 显式声明:用
ALTER TABLE t ALGORITHM=INPLACE, LOCK=NONE, MODIFY COLUMN c VARCHAR(50),失败立刻报错,不硬扛 - 千万级以上表,别自己手写 ALTER,用
pt-online-schema-change,它分批拷贝+自动切换,业务无感 - 哪怕小表,也避开高峰期执行,并监控
SHOW PROCESSLIST中是否出现copy to tmp table
改完必须立刻验证隐式行为变化
类型变了,查询行为可能跟着变。光看 DESC t 不够,得实测:
- 索引是否还走:
EXPLAIN SELECT * FROM t WHERE c = 'x',尤其注意VARCHAR改CHAR后字符串比较是否触发隐式转换 -
ENUM增项只允许追加到末尾,中间插入或调序会让原有数据映射错乱(比如原ENUM('a','b','c')存的b是 2,改成ENUM('a','c','b')后全变成c) - MySQL 8.0.29+ 默认启用
ALGORITHM=INSTANT,但缩窄类型时它不生效,反而报ERROR 1845,得手动切回INPLACE
真正耗时的从来不是那条 ALTER 语句,而是查风险、抄约束、验结果这三步。跳过任意一步,后面花十倍时间救火都未必来得及。











