必须用utf8mb4_unicode_ci而非utf8mb4_general_ci,因其遵循unicode标准按拼音、声调、笔画排序中文,而后者仅按字节值排序易出错;laravel需在database.php中配置charset/collation、pdo初始化命令,并验证collation_connection生效。

在Laravel项目中正确使用utf8mb4_unicode_ci排序规则,直接关系到中文搜索结果是否按拼音排序、JOIN能否命中相同汉字、WHERE条件是否忽略大小写却仍能匹配“张三”和“张叁”。这个排序规则不是开关式配置,而是嵌套在连接层、数据库、表、列四级中的隐性逻辑链。
为什么必须用utf8mb4_unicode_ci而不是utf8mb4_general_ci
utf8mb4_general_ci在MySQL 8.0中已被标记为废弃,它对中文的排序基于字节值而非语义——比如“张”(U+5F20)和“李”(U+674E)在字节层面可能被排错顺序;而utf8mb4_unicode_ci严格遵循Unicode 6.0+标准,对简体中文按《GB18030》映射后的Unicode码位做权重分级,先比拼音首字母,再比声调,最后比笔画数。
如果你用utf8mb4_general_ci建表,执行SELECT * FROM users WHERE name LIKE '%张%' ORDER BY name,结果里“章”可能排在“张”前面,“張”(繁体)甚至根本搜不到——因为general_ci把“張”当成独立字符,不与“张”归并。
Laravel连接层强制生效的关键三步
第一步:修改config/database.php中mysql配置块,显式声明charset和collation:
'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci'
第二步:必须添加PDO初始化命令,否则Laravel 9+在某些PHP版本下仍会回退到utf8连接:
'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"]
【DB_CHARSET环境变量完全无效】 Laravel官方代码从未读取.env里的DB_CHARSET,只认config/database.php里硬编码的charset字段。
第三步:验证连接生效,在tinker中运行DB::select("SHOW VARIABLES LIKE 'collation%';"),确认collation_connection和collation_database均为utf8mb4_unicode_ci。
已有表升级时最容易踩的坑
方法一:ALTER TABLE单表转换(适合小表)
DB::statement("ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
方法二:逐列指定(避免TEXT字段因长度限制失败)
DB::statement("ALTER TABLE users MODIFY COLUMN name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;");
注意:CONVERT TO会重写整张表数据页,线上大表务必在低峰期执行;若表含FULLTEXT索引,必须先DROP再重建,否则报错ERROR 1287。
方法三:Laravel迁移中安全覆盖(推荐)
在新迁移文件里写:
Schema::table('users', function (Blueprint $table) {
$table->string('name')->charset('utf8mb4')->collation('utf8mb4_unicode_ci')->change();
});
这会生成ALTER TABLE ... MODIFY COLUMN语句,比CONVERT更精准,且支持回滚。
中文模糊搜索必须配合的SQL写法
即使排序规则设对了,Laravel默认的where('name', 'like', "%{$keyword}%")仍走B-tree索引前缀匹配,无法利用排序规则的语义能力。
要真正触发utf8mb4_unicode_ci的拼音归并效果,必须用:
DB::table('users')->whereRaw("name COLLATE utf8mb4_unicode_ci LIKE ?", ["%{$keyword}%"])
否则“张”和“章”永远无法同框出现——因为MySQL默认用列定义的collation做比较,而LIKE操作符在无COLLATE显式声明时,会降级为二进制比较。











