alter table … convert to character set 会修改表默认字符集及所有char、varchar、text字段的字符集并重写存储内容,不改动int、blob等非文本字段和索引定义,但文本列索引的排序规则可能隐式调整。

ALTER TABLE … CONVERT TO CHARACTER SET 会改哪些内容?
这条语句不仅修改表的默认字符集,还会把所有 CHAR、VARCHAR、TEXT 类型字段的字符集一并转换,并重写对应列的存储内容。它不会动 INT、BLOB 这类非文本字段,也不会改索引定义本身——但索引字段如果依赖文本列,底层排序规则可能随字符集变化而隐式调整。
常见误操作是以为只改了表头声明,结果发现中文乱码或查询变慢,本质是字段内容被重新编码了。
-
CONVERT TO会重建表(MySQL 5.7+ 在支持ALGORITHM=INPLACE的情况下可避免锁表,但仅限部分变更,字符集转换通常仍需COPY) - 如果原字段用的是
utf8(即 utf8mb3),转成utf8mb4后,TEXT类型的索引前缀长度上限从 255 变为 191 —— 超出会报错Specified key was too long - 执行前务必确认字段实际存储内容是否兼容目标字符集,比如
latin1字段里存了 UTF-8 编码的字节流,直接CONVERT TO utf8mb4会导致双编码乱码
批量改多个表时怎么避免手动写 N 条 ALTER TABLE?
靠拼接 SQL:查出目标表名,生成 ALTER TABLE 语句,再执行。核心是用 information_schema.tables 筛表,注意过滤掉系统库和非 InnoDB 表(有些引擎不支持字符集转换)。
示例(生成语句,不直接执行):
SELECT CONCAT('ALTER TABLE `', table_schema, '`.`', table_name, '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
FROM information_schema.tables
WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
AND engine = 'InnoDB'
AND table_collation NOT LIKE 'utf8mb4%';
复制结果、检查、再批量执行。别忘了加 COLLATE,否则会用默认排序规则(可能是 utf8mb4_0900_ai_ci,旧版本不识别)。
- 先在测试库跑一遍,观察是否有
Truncated incorrect character string类警告 - 生产环境建议单表执行,加
pt-online-schema-change或等低峰期操作,避免长事务阻塞 - 如果表有外键,需先禁用
SET FOREIGN_KEY_CHECKS = 0;,改完再开,否则可能因约束校验失败中断
只想改表默认字符集,不动字段内容怎么办?
用 ALTER TABLE ... CHARACTER SET = xxx(注意等号右边没有 CONVERT TO)。它只更新表元数据里的 DEFAULT CHARSET,后续新字段会继承这个字符集,但已有字段保持原样。
这种操作轻量、快,适合建表时遗漏设置、后期补救。但它不解决已有字段乱码问题——那得靠 CONVERT TO 或更谨慎的手动 MODIFY COLUMN。
- 执行后查
SHOW CREATE TABLE t,确认DEFAULT CHARSET已变,但各字段定义没动 - 如果字段本身指定了字符集(如
VARCHAR(100) CHARACTER SET latin1),这个显式声明优先级高于表级默认值 - 某些 ORM(如 Django)建表时硬编码字符集,这类表即使改了表级默认值,下次 migrate 还可能覆盖回去
utf8mb4 下 varchar(255) 索引失效?
不是失效,是超长报错。InnoDB 单列索引最大长度 767 字节(老版本)或 3072 字节(5.7+ 开启 innodb_large_prefix),utf8mb4 最坏情况每字符占 4 字节,所以 varchar(255) 最多 1020 字节 —— 超过 767 就触发错误。
解决方案不是砍字段长度,而是控制索引前缀:
ALTER TABLE t MODIFY COLUMN title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE t DROP INDEX idx_title; ALTER TABLE t ADD INDEX idx_title (title(191));
- 191 是安全上限:191 × 4 = 764
- 如果业务真需要全字段索引(比如精确匹配长标题),得确认 MySQL 版本 ≥ 5.7 且
innodb_large_prefix = ON、innodb_file_format = Barracuda、表行格式为DYNAMIC或COMPRESSED - 用
SHOW VARIABLES LIKE 'innodb_large_prefix';检查,别凭经验猜
character_set_client)和表字符集不一致。这些不在 ALTER TABLE 范围内,得单独调。











