直接回答:MySQL 的 utf8 是“残缺版”UTF-8,一个字符最多只存 3 字节,无法存储 emoji、部分生僻汉字等 4 字节字符,写入时会报 Incorrect string value 错误;utf8mb4 才是完整的 UTF-8(最多 4 字节),自 MySQL 8.0 起成为默认字符集,新项目应一律使用 utf8mb4。

展开解析:这是历史包袱:2003 年前后 MySQL 为了节省索引空间把 utf8 限制为 3 字节,后来发现 emoji、部分生僻汉字、数学符号需要 4 字节才补上 utf8mb4。老表的排序规则常是 utf8_general_ci,迁移时建议用 utf8mb4_0900_ai_ci(8.0 默认,基于 Unicode 9.0,支持更准确的排序和大小写不敏感比较)或 utf8mb4_general_ci。索引长度是常见坑:InnoDB 旧行格式前缀索引上限 767 字节,utf8 下可索引 255 个字符,utf8mb4 只有 191 个;5.7 开启 innodb_large_prefix 并使用 DYNAMIC 行格式后上限提升到 3072 字节(768 字符)。代价方面,utf8mb4 字段在排序缓冲区、临时表和索引内存中都按 4 字节预算,CHAR(10) 占 40 字节,比 utf8 多三分之一,大表上值得权衡。

ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
SHOW VARIABLES LIKE 'character_set%';  -- 检查 server/client/connection/results 四层

实践要点:迁移要同时改库、表、列三层默认值以及客户端连接字符集,只改其一会在连接层被转回 utf8 而前功尽弃;utf8 与 utf8mb4 字段直接 JOIN 会因字符集不一致导致索引失效,改造期间要特别检查跨表关联。新建库表时显式写 CHARACTER SET utf8mb4,不要依赖实例默认值。

追问方向:索引前缀 191 这个限制怎么算出来的?CHARACTER SET 与 COLLATE 是什么关系?latin1 老表如何无损迁移? (约 373 字)