必须查风险:用SELECT COUNT(*) FROM t WHERE LENGTH(c) > 50查超长值,SELECT MIN(c), MAX(c)查越界数值,REGEXP查四字节emoji,SHOW CREATE TABLE抄全声明;只改类型用MODIFY,改名必用CHANGE;大表优先pt-online-schema-change。

不能直接“改类型”,而是重建字段存储——这意味着不检查数据就执行 ALTER TABLE ... MODIFY COLUMN,大概率静默丢数据,尤其缩窄长度、跨字符集或没开严格模式时。
怎么查改字段前的风险?
别跳过这步。改之前必须确认现有数据是否能被新类型容纳,否则截断无声无息。
- 查超长字符串:
SELECT COUNT(*) FROM t WHERE LENGTH(c) > 50;(假设你要改成VARCHAR(50)) - 查越界数值:
SELECT MIN(c), MAX(c) FROM t;,对照新类型取值范围(比如TINYINT是 -128~127) - 查 emoji 或四字节字符:
SELECT c FROM t WHERE c REGEXP '[\xF0-\xF7][\x80-\xBF]{3}';,若要从utf8mb4改成utf8,这些全变? - 查原定义:
SHOW CREATE TABLE t;,抄下完整字段声明(含NOT NULL、DEFAULT、COMMENT),避免漏写约束
MODIFY COLUMN 和 CHANGE COLUMN 该怎么选?
只改类型不改名,用 MODIFY COLUMN;要改名+改类型,才用 CHANGE COLUMN。语法差异小,但错一个字就失败。
-
MODIFY COLUMN c INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态';—— 约束和注释都得重写,漏了NOT NULL就变可空 -
CHANGE COLUMN c c INT UNSIGNED NOT NULL DEFAULT 0;—— 必须重复写两次列名,少一次报ERROR 1064 -
CHANGE COLUMN c d INT UNSIGNED;—— 名字真变了,不是笔误,是故意重命名 -
TEXT/BLOB字段不能带DEFAULT,语句里写了就直接报ERROR 1101,删掉再试
线上大表改类型怎么不翻车?
哪怕加了 ALGORITHM=INPLACE,MySQL 该退化还是退化——尤其是缩窄、跨字符集、改 TEXT 类型时,会自动切到 COPY 模式,锁表、占磁盘、拖慢业务。
- 强制指定算法:
ALTER TABLE t ALGORITHM=INPLACE, LOCK=NONE, MODIFY COLUMN c VARCHAR(50);,失败立刻报错,不硬扛 - 千万级以上表,别自己手写
ALTER,用pt-online-schema-change,它分批拷贝、自动切换,业务零感知 -
ENUM增项必须追加到末尾,中间插入或调序会导致原有数据映射错乱(比如原ENUM('a','b','c')中的'b'存的是 2,改成ENUM('a','c','b')后全变'c') - 改完立刻验证:
EXPLAIN SELECT * FROM t WHERE c = 'x';,看索引是否还走,尤其注意隐式转换导致的失效(如VARCHAR改成CHAR后用字符串比较)
MySQL 8.0.29+ 报 ERROR 1845 怎么办?
这是默认启用 ALGORITHM=INSTANT 导致的——它不支持任何类型变更,哪怕 VARCHAR(50) → VARCHAR(200) 也不行。
- 显式禁用 INSTANT:
ALTER TABLE t ALGORITHM=INPLACE, MODIFY COLUMN c BIGINT; - 别信“只是扩大长度就安全”,INSTANT 对所有类型变更一概拒绝,必须手动指定算法
- 如果表有外键,MySQL 9.6.0 已将外键逻辑上移至 SQL 层,Binlog 记录完整,但改字段仍需检查关联表约束是否兼容
真正麻烦的从来不是语法怎么写,而是改之前没看懂那条数据到底在哪儿被截断、哪个索引悄悄失效、哪次锁表让下游等了三分钟。


















