docker环境下laravel数据库乱码、emoji失败等问题,根源在于utf8mb4未贯穿容器配置、数据库初始化、连接参数、laravel迁移四层;必须在docker-compose.yml中通过environment(mysql_charset等)和command(--character-set-server)双保险设定,并在init.sql、.env(db_charset=utf8mb4)、迁移文件(schema::defaultstringlength(191))中同步落实。

在Docker环境下运行Laravel时,数据库中文乱码、emoji插入失败、索引长度超限等问题,几乎都源于MySQL/MariaDB容器与Laravel应用层字符集不一致——utf8mb4必须贯穿容器配置、数据库初始化、连接参数、Laravel迁移四层。
Docker Compose中强制指定utf8mb4初始化
在docker-compose.yml的mysql或mariadb服务下,必须显式设置字符集和排序规则,否则容器启动时默认用latin1或utf8(非utf8mb4):
在environment块中添加以下三项:
MYSQL_CHARSET=utf8mb4
MYSQL_COLLATION=utf8mb4_unicode_ci
MYSQL_INITDB_SKIP_TZINFO=1(跳过时区初始化,避免部分镜像因权限问题卡住)
⚠️ 注意:仅设环境变量不够,某些MariaDB 10.6+镜像会忽略MYSQL_CHARSET,必须配合command指令强制执行初始化命令。
在service定义里追加command行:
command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci']
容器内数据库创建时指定字符集
如果使用自定义SQL初始化脚本(如init.sql),不能只写CREATE DATABASE myapp;,必须明确指定字符集:
CREATE DATABASE IF NOT EXISTS `myapp` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
这一步不可省略。Docker容器启动时若未通过environment或command设定默认字符集,即使后续在Laravel里设了utf8mb4,新建库仍继承服务器默认值(通常是latin1),导致建表失败或字段被截断。
Laravel .env中启用utf8mb4连接参数
仅设置DB_DATABASE还不够,必须让PDO连接时主动声明字符集:
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel_local
DB_USERNAME=laravel
DB_PASSWORD=secret
在.env末尾追加这一行:
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
【DB_CHARSET=utf8mb4必须存在,否则artisan migrate生成的表仍用utf8】
验证并修复现有表字符集
第一步:进入MySQL容器执行检查
docker exec -it your-mysql-container mysql -u root -p -e "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='laravel_local';"
第二步:确认结果为utf8mb4和utf8mb4_unicode_ci
第三步:若已有表字符集错误,执行批量转换(在容器内执行):
ALTER DATABASE laravel_local CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
注意:CONVERT操作会锁表,生产环境慎用;开发环境可直接删库重迁。
Laravel迁移文件中显式声明字符串长度
utf8mb4占用4字节/字符,而MySQL InnoDB索引长度限制为767字节(5.7默认)或3072字节(5.7+ with innodb_large_prefix)。这意味着VARCHAR(255)在utf8mb4下实际占1020字节,超出旧版索引限制。
解决方法只有两个:
方法一:在AppServiceProvider的boot()中统一设长:
Schema::defaultStringLength(191);
方法二:在迁移文件中为每个String字段手动指定长度:
$table->string('title', 191);
$table->text('content')->charset('utf8mb4');
【不设191或更小长度,migration会因索引超限报错】











