ALTER TABLE CONVERT TO 默认锁全表,因ALGORITHM=COPY需重建表;满足utf8mb4间转换、DYNAMIC行格式等条件时可用ALGORITHM=INPLACE避免锁表;否则应分步MODIFY COLUMN并确保客户端连接层声明utf8mb4。

为什么ALTER TABLE CONVERT TO会锁全表
ALTER TABLE ... CONVERT TO CHARACTER SET 在 MySQL 5.7/8.0 默认是 ALGORITHM=COPY,会重建整张表:加写锁、拷贝数据、重建索引、原子替换。对大表来说,锁表时间可能长达分钟级,业务直接卡住。这不是“慢”,是阻塞——SELECT 可能被堵在元数据锁(MDL)上,INSERT/UPDATE 更是直接排队。
用ALGORITHM=INPLACE跳过锁表(但有前提)
MySQL 5.7+ 支持 ALGORITHM=INPLACE 的在线 DDL,但仅当满足以下全部条件时,CONVERT TO 才能真正不锁写入:
-
CHARACTER SET变更前后都是 utf8mb4(即从 utf8mb4_unicode_ci → utf8mb4_0900_ai_ci),且字段类型未变(如 VARCHAR 不扩长、TEXT 不改类型) - 表使用
ROW_FORMAT=DYNAMIC或COMPRESSED(COMPACT和REDUNDANT不支持 inplace 字符集变更) - 没有全文索引(FULLTEXT)、空间索引(SPATIAL),也没有外键约束(或外键已禁用
foreign_key_checks=0) - 执行用户有
LOCK TABLES权限,且未开启innodb_lock_wait_timeout过短等干扰项
验证是否可行:先跑 SHOW CREATE TABLE table_name; 看 ROW_FORMAT;再确认当前字符集是不是已经是 utf8mb4(避免从 utf8 → utf8mb4 的跨编码转换)。
真正安全的“无锁”方案:分步 + 应用层配合
如果你的表还带着 utf8 或 latin1,又不能停业务,别硬刚 CONVERT TO。用三步走,把锁粒度压到单语句级:
- 先确保服务端和连接层已配好 utf8mb4(
my.cnf三段都设好、重启、SHOW VARIABLES LIKE 'character%'全为 utf8mb4) - 对每个字符串字段单独
MODIFY COLUMN,显式指定新字符集和排序规则:ALTER TABLE t MODIFY COLUMN name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; - 每条
MODIFY仍会锁该字段所在行(如果是二级索引列,可能锁更多),但不会锁全表;可拆成多个小批次,在低峰期逐个执行
注意:MODIFY COLUMN 必须重写完整定义(类型、长度、NULL/NOT NULL、DEFAULT),漏掉 NOT NULL 可能导致字段变成允许 NULL。
容易被忽略的坑:客户端没声明 utf8mb4,改了也白改
即使表、配置、SQL 全改成 utf8mb4,只要应用连接时没声明字符集,MySQL 就按 character_set_client 解析传入的字节流。常见错误:
- PHP PDO 缺
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4" - Java JDBC URL 没加
?useUnicode=true&characterEncoding=utf8mb4 - Python PyMySQL 初始化没传
charset='utf8mb4' - 命令行连 MySQL 时用了
localhost(走 socket),结果[client]段配置没生效——换成127.0.0.1强制走 TCP
验证方法:连上后立刻执行 SELECT @@character_set_client, @@collation_connection;,两个值必须是 utf8mb4 开头。否则前面所有操作,只是给未来埋雷。


















