mysql修改字段类型不能保证不锁表,需提前验证数据兼容性、显式指定algorithm=inplace和lock=none,并对千万级表优先使用pt-online-schema-change工具。

不能保证不锁表,但可以大幅降低锁表风险和影响范围——关键在于提前验证、选对语法、用对工具。
先查数据兼容性,否则改完就丢数据
MySQL 的 MODIFY COLUMN 或 CHANGE COLUMN 不会主动校验现有值是否适配新类型。静默截断或转极值是默认行为(除非开启 STRICT_TRANS_TABLES)。
- 查字符串超长:
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),漏写就丢约束
用 ALGORITHM=INPLACE, LOCK=NONE 显式声明,失败立刻报错
MySQL 5.6+ 默认尝试 INPLACE,但很多操作(缩窄长度、跨字符集、改 TEXT 类型)会自动退化为 COPY 模式,整表锁死。显式指定能避免“硬扛”。
- 安全写法:
ALTER TABLE t ALGORITHM=INPLACE, LOCK=NONE, MODIFY COLUMN c VARCHAR(50) - 一旦不支持
LOCK=NONE,立即报错(如 ERROR 1845),而不是默默切到COPY模式 - 确认支持上限:
SELECT @@innodb_online_alter_log_max_size,大表需调高(默认 128MB) - 执行中监控:
SHOW PROCESSLIST看是否有copy to tmp table状态
千万级表别碰原生命令,直接上 pt-online-schema-change
哪怕加了 ALGORITHM=INPLACE,MySQL 对大表的判断仍不可靠。线上环境,pt-osc 是更可控的选择。
- 它不依赖 MySQL 的 Online DDL,而是用影子表 + 触发器 + 分批拷贝,业务读写几乎无感
- 命令示例:
pt-online-schema-change --alter "MODIFY COLUMN c VARCHAR(50)" D=test,t=users --execute - 必须加
--dry-run先试跑,检查触发器、外键、主键是否兼容 - 注意:它要求表必须有主键或唯一非空索引,否则拒绝执行
改完必须立刻验证索引和数据表现
类型变更可能引发隐式转换,导致索引失效或查询结果异常,光看 DESCRIBE 不够。
- 结构验证:
DESCRIBE t确认Type、Null、Default是否如预期 - 数据抽样:
SELECT id, c FROM t WHERE c IS NOT NULL ORDER BY id LIMIT 5,重点核对值是否被截断、变 0 或乱码 - 索引验证:
EXPLAIN SELECT * FROM t WHERE c = 'x',看key列是否命中索引,尤其注意VARCHAR→CHAR后字符串比较是否触发全表扫描 - 时间字段特别危险:
DATETIME→DATE会永久丢失时分秒,无法回退
真正卡住人的不是语法怎么写,而是你改完才发现某几万行数据在应用层突然变成空字符串或 0 —— 这些问题只在业务流量进来后才暴露。验证必须覆盖真实数据分布,不能只看 LIMIT 10。











