问题根源在于ci4的charset配置仅作用于连接层,必须同步确保mysql服务端、表结构、字段定义四层均为utf8mb4;需验证select @@character_set_client等三值是否全为utf8mb4,检查show create table确认字段显式声明utf8mb4,并在my.cnf[mysqld]段配置character-set-server=utf8mb4及init_connect='set names utf8mb4'后重启服务。

CodeIgniter 4 数据库配置里写了 charset => 'utf8mb4' 却仍乱码,问题不在“有没有设”,而在于“设对没设全”——CI4 的 charset 配置只影响连接层初始化,无法覆盖 MySQL 服务端、表结构、甚至驱动行为的其他环节。必须四层齐查、同步生效。
一、CI4 连接配置只是起点,不是全部
CI4 的 app/Config/Database.php 中设置:
'charset' => 'utf8mb4', 'DBCollat' => 'utf8mb4_unicode_ci'
这等价于连接后执行 SET NAMES utf8mb4,仅确保 character_set_client/connection/results 三者一致。但若 MySQL 服务端本身默认是 latin1,或表字段定义为 utf8(即 utf8mb3),这一行就形同虚设。
验证是否真正生效:
- 连上数据库后直接运行:
SELECT @@character_set_client, @@character_set_connection, @@character_set_results;—— 三者必须全是utf8mb4 - 如果其中任一是
latin1或utf8,说明 CI4 的 charset 没触发,常见原因是:MySQL 服务端禁用了init_connect,或用户权限不足无法执行 SET 命令
二、表和字段字符集没升级,存进去就截断
即使 CI4 连接正确、插入语句也带中文,只要字段定义仍是 VARCHAR(255) CHARACTER SET utf8,四字节 emoji 或生僻汉字就会被静默截断或转成 。
检查方式:
→SHOW CREATE TABLE your_table; —— 看字段定义末尾是否明确写着 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
修复方法(不能只靠 CONVERT):
- 安全做法:逐字段修改,例如
ALTER TABLE users MODIFY COLUMN bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 批量转换(慎用):
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
⚠️ 注意:该操作会重建表、锁表、翻倍占用磁盘空间,大表务必在低峰期执行
三、MySQL 服务端配置未兜底,连接一断就回退
CI4 的 charset 是运行时设置,一旦连接异常重连、或使用了连接池,就可能丢失上下文。真正可靠的是服务端强制兜底:
- 确认
my.cnf的[mysqld]段有:character-set-server = utf8mb4collation-server = utf8mb4_unicode_ciinit_connect = 'SET NAMES utf8mb4' - 重启 MySQL:
systemctl restart mysqld(仅 reload 不生效) - 云数据库(如阿里云 RDS)需在控制台修改参数组,不能直接改配置文件
四、CI4 驱动实际行为与预期不符
CI4 默认使用 PDO 或 MySQLi,但不同驱动对 charset 的处理逻辑略有差异:
- 若用 MySQLi,需确认 PHP 的
mysqli.default_charsetini 设置未覆盖为utf8 - 若用 PDO,CI4 底层会加
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",但若数据库用户无 EXECUTE 权限,该命令会被忽略且不报错 - 建议在 CI4 的数据库连接后手动验证:
$db = \Config\Database::connect();<br>var_dump($db->query("SELECT @@character_set_client")->getRow());











