必须在建立连接后、执行任何查询前调用 mysqli_set_charset("utf8mb4"),否则因客户端默认 latin1 或 utf8mb3 导致中文乱码、emoji 丢失或字符串截断;需配合 init_connect='set names utf8mb4' 双保险,并验证三个字符集变量均为 utf8mb4。

必须在建立连接后、执行任何查询前调用 mysqli_set_charset(),否则中文插入或读取大概率乱码
为什么不能只靠配置文件或SQL语句
MySQLi连接初始化时会继承服务器的 character_set_client 和 character_set_connection,但这些值可能被客户端环境覆盖(比如终端 locale、PHP 默认编码)。即使 my.cnf 里写了 utf8mb4,PHP 的 MySQLi 扩展默认仍可能用 latin1 建立连接。
常见错误现象:
- 插入中文显示为
???或空格 -
SHOW VARIABLES LIKE 'character_set_%'显示character_set_client是utf8(不是utf8mb4) - 数据库和表明明是
utf8mb4,但 PHP 读出来的字符串长度异常或截断
正确调用 mysqli_set_charset() 的时机和写法
它必须在 mysqli_connect() 成功之后、任何 mysqli_query() 之前执行。参数必须是字符串 "utf8mb4",不是 "utf8" —— 后者在 MySQLi 中实际映射为 utf8mb3,不支持 emoji 和部分生僻汉字。
实操建议:
- 不要用
SET NAMES utf8替代,mysqli_set_charset()会同时设置character_set_client、character_set_results和character_set_connection,更可靠 - 如果使用面向对象风格,写法是
$conn->set_charset("utf8mb4") - 如果连接失败,
mysqli_set_charset()会返回false,应检查并报错,而不是静默忽略
配合连接参数避免遗漏
仅靠代码中调用还不够稳妥。当连接字符串里没指定字符集,且 mysqli_set_charset() 被意外跳过(比如异常提前退出),就又掉坑里了。
推荐组合使用:
- 连接时在 DSN 中加
;charset=utf8mb4(面向过程):mysqli_connect($host, $user, $pass, $db, $port, $socket)不支持直接传 charset,所以得靠后续set_charset - 面向对象方式可传第四参数:
new mysqli($host, $user, $pass, $db, $port, $socket)同样不接受 charset 参数,仍需显式调用 - 最保险的是:配置文件中加
init_connect='SET NAMES utf8mb4',再加代码层set_charset双保险
验证是否生效的最小检查点
别只信配置,运行一次检查 SQL:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results;
三个值都应返回 utf8mb4。如果其中一个是 utf8,说明某处漏设了——大概率是 mysqli_set_charset() 没执行,或执行晚于第一条查询。
容易被忽略的一点:PDO 用户常误以为 MySQLi 也支持 PDO::MYSQL_ATTR_INIT_COMMAND,但它不支持;MySQLi 没有等效机制,init_connect 是唯一服务端兜底手段。











