mysql utf8和utf8mb4区别_mysql字符集选择建议

9次阅读

mysql的utf8不是真正的UTF-8,仅支持3字节字符;utf8mb4才是完整实现,需显式指定字符集、修正字段定义、同步客户端连接,并合理选择排序规则。

mysql utf8和utf8mb4区别_mysql字符集选择建议

utf8 在 MySQL 里根本不是真正的 UTF-8

MySQL 的 utf8 字符集实际只支持最多 3 字节的 Unicode 字符,也就是 U+0000 到 U+FFFF 范围,不包含 emoji、部分中文生僻字(如“?”)、古汉字、数学符号等 4 字节字符。它只是个历史遗留命名错误,官方也承认这是个“误称”。utf8mb4 才是 MySQL 中真正完整的 UTF-8 实现,支持全部 Unicode 字符(U+0000–U+10FFFF)。

建表或改表时必须显式指定 utf8mb4

即使服务器默认字符集设为 utf8mb4,单个表、列仍可能沿用旧配置。常见遗漏点:

  • CREATE table 语句中未加 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
  • 修改已有表时只改了表级字符集,漏掉 MODIFY columnCHANGE COLUMN 对字段重定义
  • 索引字段长度超限:VARCHAR(255)utf8mb4 下,若用 utf8mb4_unicode_ci 排序规则,索引长度按每字符 4 字节算,可能突破 InnoDB 单索引 767 字节限制(尤其在 innodb_large_prefix=OFF 时)

正确做法示例:

ALTER TABLE users  CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

但注意:这仅更新表和字段默认值,不改变已存在字段的定义;如需确保字段本身也生效,还需单独执行:

ALTER TABLE users  MODIFY COLUMN name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

客户端连接必须同步设置 utf8mb4

只改服务端字符集没用,如果连接层(如 JDBC、php pdopython mysqlclient)没声明 charset=utf8mb4,数据写入/读取时仍会按 utf8 解码,导致乱码或截断。典型错误现象:

  • 插入 emoji 报错 Incorrect String value: 'xF0x9Fx98x80'...
  • 查出来字段显示为 ??? 或空字符串
  • 同一字段,命令行 mysql 客户端能正常显示,应用却乱码

检查连接字符集是否生效:

SELECT @@character_set_client, @@character_set_connection, @@character_set_results;

三者都应返回 utf8mb4。否则需在连接字符串中显式指定,例如:

  • JDBC:?useUnicode=true&characterEncoding=utf8mb4
  • PHP PDO:charset=utf8mb4 加入 DSN
  • my.cnf 全局配置(仅对未显式覆盖的连接有效):default-character-set = utf8mb4([client] 段)和 collation-server = utf8mb4_unicode_ci([mysqld] 段)

utf8mb4_unicode_ci 和 utf8mb4_0900_as_cs 的选择

排序规则影响大小写敏感性、重音处理和性能。MySQL 8.0+ 默认推荐 utf8mb4_0900_as_cs(accent-sensitive + case-sensitive),而 utf8mb4_unicode_ci 是较老的、不区分大小写的通用规则。

  • 需要精确匹配(如密码盐、Token、唯一键校验)→ 选 _as_cs 规则
  • 用户昵称、文章标题等需模糊搜索 → utf8mb4_unicode_ci 或更现代的 utf8mb4_0900_ai_ci(accent-insensitive)更合适
  • 已有系统升级时注意:更换 collation 可能导致唯一索引冲突(比如原来 “A” 和 “a” 被视为不同值,新规则下视为相同)

查看当前支持的规则:

SHOW COLLATION WHERE Charset = 'utf8mb4' AND Collation LIKE '%_ci' OR Collation LIKE '%_cs';

真正麻烦的是存量系统改造——字段改了、表改了、连接改了,但某个老旧的定时脚本或第三方工具还在用 utf8 连接,一跑就出错。这种地方往往藏得深,得靠日志里的报错字符和具体 SQL 上下文才能揪出来。

text=ZqhQzanResources