MySQL字段字符集不生效需同步检查连接层、表层和列级配置,Laravel迁移须显式用collation()指定排序规则,大表修改应避免直接change()而采用分步策略。

MySQL 字段字符集不生效?先确认连接层和表层是否一致
直接改 charset 或 collation 不起作用,大概率是 Laravel 迁移没穿透到列级,或者 MySQL 服务端默认配置压过了你的声明。Laravel 的 string()、text() 等方法默认用的是数据库连接的默认字符集(通常是 utf8mb4),但如果你在迁移里没显式指定列的 collation,它会继承表级 collation;而表级又可能继承自数据库或连接。所以光写 $table->string('name') 是不够的。
- 检查当前连接字符集:
SHOW VARIABLES LIKE 'character_set_client'和SHOW VARIABLES LIKE 'collation_connection' - 确认迁移生成的 SQL 是否真带了
COLLATE utf8mb4_unicode_ci—— 用php artisan migrate --pretend看输出 - 如果目标列需要中文模糊搜索或 emoji 支持,必须用
utf8mb4系列 collation,utf8会丢数据
Laravel 迁移中为单个字段指定字符集和排序规则
5.8+ 版本起,Laravel 原生支持在列定义时传 collation 参数,但仅限部分字段类型(如 string()、text()、tinyText() 等),且底层依赖 PDO 和 MySQL 版本支持。不是所有驱动都认这个参数。
- 正确写法:
$table->string('title')->collation('utf8mb4_bin') - 错误写法:
$table->integer('status')->collation('...')(整数类型不支持 collation) - 注意:Laravel 不提供单独设
charset的 API,collation隐含了字符集,比如utf8mb4_unicode_ci对应utf8mb4 - 若用
DB::statement()手动执行ALTER TABLE ... MODIFY COLUMN ... CHARACTER SET ... COLLATE ...,得自己处理事务和回滚逻辑
已有表加字段或改字段字符集,迁移怎么写才安全
线上表不能随便 CHANGE COLUMN,尤其大表会锁表。Laravel 的 change() 方法在 MySQL 上本质是 ALTER TABLE ... MODIFY COLUMN,触发全表重建。别指望加个 collation() 就能“轻量更新”。
- 小表可直接用:
$table->string('desc')->collation('utf8mb4_general_ci')->change() - 大表建议分两步:先
ADD COLUMN新字段并同步数据,再删旧字段(需业务停写或双写) - 务必在迁移中加
if (Schema::hasColumn(...))判断,避免重复执行报错 -
php artisan migrate:rollback不会自动还原 collation 修改,要手动在down()里写反向语句
MySQL 8.0+ 默认 collation 变了,Laravel 迁移容易踩的坑
MySQL 8.0 默认 collation_server = utf8mb4_0900_ai_ci,而 Laravel 旧版本(如 6.x/7.x)生成的迁移 SQL 若没显式指定 collation,可能被服务端降级成 utf8mb4_general_ci 或更老的规则,导致 ORDER BY、WHERE LIKE 行为不一致。
- 升级 MySQL 后,跑一遍
php artisan migrate:fresh --seed看新表是否真用了预期 collation - 在
config/database.php的 MySQL 配置里加'charset' => 'utf8mb4'和'collation' => 'utf8mb4_0900_ai_ci'(前提是驱动支持) - 不要在模型里用
$casts或accessor模拟字符集转换——那是应用层补丁,解决不了索引失效和比较逻辑错乱
真正麻烦的不是怎么写那行 collation(),而是你得同时盯住 Laravel 迁移生成逻辑、PDO 驱动行为、MySQL 服务端变量、以及线上表的数据量级。少一个环节对不上,字段看着改了,查出来还是乱码或排序错位。











