1. 问题现象
项目通过 Docker Compose 启动 MySQL:
powershell
docker compose up -d
MySQL 容器会自动执行 init 目录中的 SQL 初始化脚本。SQL 文件中的中文内容正常,但数据导入数据库后变成了乱码,例如:
text
柳岩 -> 柳岩
苹果 -> 苹果
这种现象是典型的 UTF-8 字节被按 latin1 解析后,再写入 UTF-8 字段所产生的乱码。
2. 检查结果
实际检查后确认:
init/01-user.sql和init/02-order.sql的文件内容为 UTF-8,SQL 文件本身没有乱码。- MySQL 服务端默认字符集为
utf8mb4。 spring-cloud-user和spring-cloud-order数据库字符集正常。- 容器中
mysql命令行客户端的默认连接字符集为latin1。 - 查询数据的十六进制内容后,确认乱码已经被实际写入数据库,不是 DataGrip 的字体或显示设置问题。
修改前的连接字符集为:
text
character_set_client latin1
character_set_connection latin1
character_set_results latin1
3. 根本原因
MySQL 官方镜像在首次创建数据目录时,会使用容器内的 mysql 客户端执行 /docker-entrypoint-initdb.d 下的 SQL 脚本。
虽然 SQL 文件和 MySQL 服务端都使用 UTF-8,但客户端在建立连接时声明的字符集是 latin1。MySQL 因此会把 SQL 文件中的 UTF-8 中文字节当成 latin1 字符解析,然后转换为 UTF-8 存入数据库,最终造成乱码。
4. 解决方案
在 docker-compose.yml 的 MySQL 服务中增加:
yaml
services:
mysql:
image: mysql:8.0.30
command: --skip-character-set-client-handshake
该参数会让 MySQL 服务端忽略客户端提交的字符集,使用服务端的默认字符集 utf8mb4。
修改后重建容器:
powershell
docker compose up -d --force-recreate mysql
此操作只重建 MySQL 容器,不会删除已有数据卷。
5. 验证结果
可以使用以下命令检查连接字符集:
powershell
docker compose exec -T mysql mysql -uroot -p123 -e "SHOW VARIABLES WHERE Variable_name IN ('character_set_client','character_set_connection','character_set_results')"
修改后的实际结果为:
text
character_set_client utf8mb4
character_set_connection utf8mb4
character_set_results utf8mb4
说明字符集设置已经生效。
6. 已有乱码数据的处理
修改连接字符集只能防止之后导入的数据继续乱码,不会自动修复已经存入数据库的乱码数据。
Docker 中的 MySQL 初始化脚本只会在 /var/lib/mysql 数据目录为空时执行一次。因此,仅再次执行 docker compose up -d 不会重新导入初始化 SQL。
如果现有数据可以删除,可执行:
powershell
docker compose down -v
docker compose up -d
警告:
docker compose down -v会删除 Compose 项目创建的数据卷,包括当前 MySQL 中的所有数据。执行前应确认数据可以丢弃,或已经完成备份。
删除数据卷并重新启动后,MySQL 会重新执行 init 目录中的 SQL,此时中文数据会按 utf8mb4 正确导入。
7. 结论
本次乱码的根本原因不是 SQL 文件编码、数据库字段字符集或 DataGrip 显示设置,而是 MySQL 初始化脚本执行时,客户端和服务端之间使用了错误的 latin1 连接字符集。
通过在 Docker Compose 中增加 --skip-character-set-client-handshake,已将连接字符集统一为 utf8mb4,可以避免后续初始化 SQL 导入中文时再次出现乱码。