alter convert to character set 本质是全表重建,需加x锁阻塞写操作,因涉及逐行重编码和页结构重写;而仅改表默认字符集不重建数据,故不锁表。

ALTER CONVERT TO CHARACTER SET 本质是全表重建
它不是改个元数据就完事,而是真实重写整张表:逐行读取原始数据、按新字符集重新编码、写入新页结构。这个过程必须加 X(排他)锁,阻塞所有 INSERT/UPDATE/DELETE 和大部分 DDL,只允许 SELECT(取决于存储引擎和隔离级别)。原因很直接:如果允许并发写入,新旧编码混杂会导致数据损坏或校验失败。
常见错误现象:SHOW PROCESSLIST 中状态为 Waiting for table metadata lock 或 copy to tmp table;INNODB_TRX 里长时间没提交的事务会卡住整个 DDL 流程。
- MyISAM 引擎下更危险——它会先拷贝全表到临时文件,期间原表完全不可写,且无事务回滚能力
- 大表(比如 >1GB)执行时长可能达分钟级,期间应用写请求全部堆积或超时
-
CONVERT TO不支持ALGORITHM=INPLACE,MySQL 5.7/8.0 都会静默走COPY算法,哪怕你显式写了ALGORITHM=INPLACE
为什么只改表默认字符集不锁表,但 CONVERT TO 就锁?
ALTER TABLE t CHARACTER SET utf8mb4 只修改表级默认字符集,不触发表重建,已有字段的 CHARACTER SET 和 COLLATE 完全不变,所以不加 X 锁,也不阻塞 DML。
而 CONVERT TO CHARACTER SET utf8mb4 会批量重定义所有字符列(CHAR、VARCHAR、TEXT 类型及 ENUM/SET),并强制重写数据页。即使字段原本就是 utf8mb4,只要 COLLATE 不同,也会触发重建。
- 漏写
COLLATE是高频坑:例如只写CONVERT TO CHARACTER SET utf8mb4,MySQL 会用默认校对规则(如utf8mb4_general_ci),导致排序异常,还白锁一次表 -
FULLTEXT索引存在时,CONVERT TO直接报错,必须先DROP INDEX再重建 - 该操作不改
BLOB、INT、DATETIME等非字符列,但如果你误以为“全表统一”就安全,可能忽略关键字段未覆盖
容易被当成“字符集锁表”的真凶其实是隐式转换
很多线上事故里,DBA 看到慢查询日志里有 UPDATE 卡住,又查到近期改过字符集,就归因为“字符集导致锁”。其实真正原因是:字段字符集和客户端连接字符集不一致(如 SQL 里传 '中文' 是 latin1,但字段是 utf8mb4),MySQL 无法使用索引,被迫全表扫描——而全表扫描的 DML 在 InnoDB 中会对所有聚簇索引记录加 X 锁,效果等同于锁表。
验证方法:EXPLAIN FORMAT=TRADITIONAL SELECT * FROM t WHERE name = 'xxx';,看 key 列是否为 NULL;再比对 CHARACTER_SET_CLIENT 与字段字符集是否一致。
- 修复不是改字符集,而是统一连接层:如
SET NAMES utf8mb4,或 JDBC URL 加&characterEncoding=utf8mb4 - 临时绕过可用显式转换:
WHERE name = CONVERT('xxx' USING utf8mb4) - 这种“伪锁表”在
SHOW PROCESSLIST中常表现为Updating状态,但INNODB_TRX里看不到对应事务
想不锁表改字符集?别用 CONVERT TO
没有零停机方案,但可以大幅降低影响窗口。核心思路是避免 CONVERT TO,改用在线变更工具或分步迁移。
-
pt-online-schema-change:创建影子表、同步增量、原子切换,全程不锁原表 DML(但 DDL 切换瞬间有短暂元数据锁) - 手写分三步:
ADD COLUMN name_utf8mb4 VARCHAR(...) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci→ 分批UPDATE SET name_utf8mb4 = CONVERT(name USING utf8mb4)(带WHERE id BETWEEN ? AND ?)→ 最后DROP COLUMN name+RENAME COLUMN - 务必提前检查:
SHOW CREATE TABLE确认当前字符集和校对规则;SELECT @@character_set_client, @@character_set_connection, @@character_set_results确保三者一致;information_schema.INNODB_TRX排查长事务
真正危险的从来不是字符集本身,而是把 CONVERT TO 当成轻量操作去执行——它动的是数据页,不是表头。大表上没做预估、没切低峰期、没配好连接层,就等于主动给服务埋雷。











