用户昵称带个表情就存不进去?MySQL utf8 转 utf8mb4 完整迁移方案

来源:互联网 时间:2026-08-28

用户注册昵称是表情符,提交后昵称变成空白,或者干脆报错 Incorrect string value。这是 MySQL 老 utf8 的锅:它的 utf8 实现最多三个字节,而 emoji 和部分生僻字是四字节字符,塞不进去。真正的完整 UTF-8 在 MySQL 里叫 utf8mb4,mb4 就是 most bytes 4。

迁移分四层:库默认字符集、表字符集、列字符集、连接字符集,全都要改,漏一层就会出现「表是 utf8mb4 但还是存不进」的诡异现象。先摸清现状:

-- 看库里各表的字符集
SELECT TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'site_db';

-- 看连接字符集
SHOW VARIABLES LIKE 'character_set%';

逐表转换用 CONVERT TO,它会连表带全部列一起改:

ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 库的默认值(只影响新建表)
ALTER DATABASE site_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

注意两点:一是整库所有表要用同一个 collation,混用 utf8_general_ci 和 utf8mb4_general_ci 的表做 JOIN 时索引会失效,性能直接跳水;二是大表 CONVERT 会锁表重建,几百万行的表要选低峰期,或者用 pt-online-schema-change 在线改。

最后是连接层,三个地方要同步改。PHP 连接:

// PDO 写法
new PDO('mysql:host=localhost;dbname=site_db;charset=utf8mb4', 'user', 'pass');

// 老代码用 SET NAMES 的,改成
$pdo->exec("SET NAMES utf8mb4");
# php.ini 或 my.cnf
; php.ini
pdo_mysql.default_charset = utf8mb4

# my.cnf [mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci

改完拿一个 emoji 实测:插入、查询、页面显示一条龙走一遍。顺手提醒,迁移前务必先备份,字符集转换是不可逆操作,中间断了或者 collation 选错,恢复起来非常痛苦。

数据来源:MySQL官方手册 

相关文章

标签:

A5创业网 版权所有