根本原因是mysql连接层默认使用utf8(utf8mb3),而非utf8mb4,导致四字节字符(如emoji、生僻中文)在传输时被拦截报错1366;必须同步配置laravel连接参数(charset、collation、pdo初始化命令)、mysql服务端(my.cnf三段配置)、数据库/表/字段字符集,并逐层验证四层字符集是否均为utf8mb4。

你的Laravel项目在插入emoji或生僻中文时突然报错SQLSTATE[HY000]: General error: 1366 Incorrect string value,根本原因不是数据库建表编码错了,而是MySQL连接层默认仍走utf8(即utf8mb3),哪怕你把表和字段全设成utf8mb4也白搭——连接一建立,四字节字符就被拦在门外。
改Laravel配置:三处必须同时动手
只改.env里的DB_CHARSET=utf8mb4是无效操作,Laravel官方代码压根不读这个变量。
打开config/database.php,定位到'mysql'配置块,在已有数组里追加这三项:
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"],
【缺一不可】。PDO初始化命令是保险栓,防止某些连接池或中间件重置连接字符集;charset和collation则告诉Laravel“我连的就是utf8mb4”,否则Schema构建器生成的建表语句可能仍用utf8。
建库与建表:服务端层面补全链路
即使Laravel配置全对,如果MySQL服务端默认字符集还是latin1或utf8,新创建的数据库、表、字段依然会继承错误编码。
登录MySQL终端,执行:
CREATE DATABASE your_db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
然后确认当前库的默认字符集:
USE your_db_name; SHOW VARIABLES LIKE 'character_set_database';——输出必须是utf8mb4。
已有数据库想改?运行:ALTER DATABASE your_db_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;,但注意:这仅影响后续新建的表,已有表结构不会自动更新。
迁移文件里埋个钩子:防191长度截断
Laravel 5.4+默认用utf8mb4,但MySQL旧版本(Specified key was too long。
在app/Providers/AppServiceProvider.php的boot()方法里加一行:
Schema::defaultStringLength(191);
这会让所有$table->string('email')这类字段默认长度设为191,191×4=764字节,刚好卡在安全线内。别手动画蛇添足去每个迁移里写->length(191),统一钩子更可靠。
验证是否真生效:四层逐级检查
改完配置别急着跑迁移,先验证每层是否真正打通:
① 连接层:在任意控制器里执行DB::select("SHOW VARIABLES LIKE 'character_set%';");,重点看character_set_client、character_set_connection、character_set_results三项,**必须全是utf8mb4**;
② 数据库层:运行SELECT DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'your_db_name';,结果得是utf8mb4;
③ 表层:执行SHOW CREATE TABLE users;(替换成你的表名),建表语句末尾必须带DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
④ 字段层:查SHOW FULL COLUMNS FROM users;,每个文本字段的Collation列必须是utf8mb4_unicode_ci,不是utf8_unicode_ci或latin1_swedish_ci。
任何一层掉链子,1366错误都会重现。
清洗历史脏数据:导入前必做
方法一:用iconv过滤非法字节(Linux/macOS)
iconv -f UTF-8 -t UTF-8//IGNORE your_data.csv > cleaned.csv
方法二:Python脚本精准剔除(推荐用于含Windows-1252污染的数据)
import re
def clean_utf8_bytes(s):
return re.sub(b'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]', b'', s.encode('utf-8', errors='ignore')).decode('utf-8')
【别用errors='replace'】,它会插入符号,后续查询、导出时可能引发二次乱码或JSON解析失败。











