Docker-Compose 部署 MySQL 8 并解决中文乱码问题

在开发环境中,使用 Docker Compose 可以快速搭建 MySQL 环境。

但使用挂在目录方式让Docker-MySQL自动执行挂载到目录(/docker-entrypoint-initdb.d)中的初始化脚本, 导入含有中文数据的 SQL 时,我遇到了令人头疼的乱码问题。

咨询了AI, 测试了各种方案之后, 总算解决了这个问题。

本文将完整介绍如何用 docker-compose.yml 部署 mysql:8.0.33,以及如何挂载初始化 SQL,并 彻底解决中文乱码 问题。

### 文章目录

  • [@toc](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [一、基础配置:指定 mysql:8.0.33 镜像](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [三、避免乱码:服务端字符集配置](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [四、初始化 SQL 文件本身的编码](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [五、完整的 docker-compose.yml 示例](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [六、最后的关键一步:在 SQL 文件中加上 `SET NAMES utf8mb4;`](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [6.1 加上这一行后,中文乱码得到了彻底解决!](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [七、验证字符集是否生效](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)
  • [小结](#文章目录 @[toc] 一、基础配置:指定 mysql:8.0.33 镜像 二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL 三、避免乱码:服务端字符集配置 四、初始化 SQL 文件本身的编码 五、完整的 docker-compose.yml 示例 六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4; 6.1 加上这一行后,中文乱码得到了彻底解决! 七、验证字符集是否生效 小结)

一、基础配置:指定 mysql:8.0.33 镜像

首先,我们在 docker-compose.yml 文件中明确指定 MySQL 镜像版本。

一定要锁定具体版本号 (如 8.0.33),而不是使用 latest 或宽泛的 8.0,以保证多台机器、多次部署的一致性。

生产环境根据情况确定, 一般不推荐使用Docker来部署。

yaml 复制代码
services:
  mysql:
    image: mysql:8.0.33
    container_name: my-mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: local_database
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"

二、映射 /docker-entrypoint-initdb.d 目录指定初始化 SQL

MySQL 官方镜像提供了一个非常实用的机制: 容器首次启动且数据目录为空时 ,会自动执行 /docker-entrypoint-initdb.d 目录下的所有 .sql.sql.gz.sh 文件。

我们只需将本地的初始化 SQL 文件挂载到该目录即可:

yaml 复制代码
    volumes:
      # 持久化数据
      - mysql_data:/var/lib/mysql
      # 挂载单个初始化 SQL(关键)
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

也可以挂载整个目录,让多个 SQL 文件按文件名顺序执行:

yaml 复制代码
    volumes:
      - ./initdb:/docker-entrypoint-initdb.d

⚠️ 重要提醒 :初始化脚本只在数据目录为空时执行一次。如果你修改了 SQL 想重新初始化,需要先删除数据卷:

bash 复制代码
docker-compose down -v

三、避免乱码:服务端字符集配置

乱码的本质是 字符集不统一

MySQL 8.0 默认字符集虽然已经是 utf8mb4,但为了万无一失,需要在服务端、客户端层面都显式声明。

通过 command 直接指定服务端字符集:

yaml 复制代码
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci

如果你想用配置文件管理,可以挂载一个自定义的 my.cnf

ini 复制代码
# my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4
yaml 复制代码
    volumes:
      - ./my.cnf:/etc/mysql/conf.d/my.cnf

其中 [client][mysql] 这两段尤其重要,因为容器在执行 /docker-entrypoint-initdb.d 里的 SQL 时,正是通过内置的 mysql 客户端导入的。如果客户端连接字符集不对,导入的中文就会乱码。


四、初始化 SQL 文件本身的编码

除了 MySQL 的配置,还要确保 SQL 文件本身的物理编码正确:

  • 文件必须保存为 UTF-8 编码(无 BOM)
  • 不要使用 GBK、ANSI 等编码保存含中文的 SQL 文件

可以用编辑器(如 VS Code)右下角确认并切换编码格式。


五、完整的 docker-compose.yml 示例

yaml 复制代码
services:
  mysql:
    image: mysql:8.0.33
    container_name: my-mysql
    restart: unless-stopped
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: local_database
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

volumes:
  mysql_data:

六、最后的关键一步:在 SQL 文件中加上 SET NAMES utf8mb4;

在完成了上面所有配置后,我以为乱码问题已经解决,但实际导入时中文依然出现乱码 。经过反复排查,我在初始化 SQL 文件的最开头加上了这一行:

sql 复制代码
SET NAMES utf8mb4;
sql 复制代码
-- init.sql
SET NAMES utf8mb4;

CREATE TABLE IF NOT EXISTS `user` (
    `id` INT PRIMARY KEY AUTO_INCREMENT,
    `name` VARCHAR(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `user` (`name`) VALUES ('张三'), ('李四'), ('王五');

6.1 加上这一行后,中文乱码得到了彻底解决!

为什么这一步如此关键?因为 SET NAMES utf8mb4;同时设置当前连接会话的三个核心字符集变量

  • character_set_client(客户端发送数据的字符集)
  • character_set_connection(连接层的字符集)
  • character_set_results(返回结果的字符集)

即使前面服务端和配置文件都做了设置,但初始化导入这个会话 如果没有显式声明连接字符集,仍可能因默认值不匹配导致中文在传输过程中被错误解码。SET NAMES utf8mb4; 直接在 SQL 执行的会话层面"一锤定音",从源头保证了中文数据以 utf8mb4 正确写入。


七、验证字符集是否生效

部署完成后,进入容器验证:

bash 复制代码
docker exec -it my-mysql mysql -uroot -p \
  -e "SHOW VARIABLES LIKE 'character%';"

确认以下变量均为 utf8mb4

  • character_set_server
  • character_set_database
  • character_set_client
  • character_set_connection

再查询数据,确认中文显示正常:

sql 复制代码
SELECT * FROM user;

小结

最终在 SQL 文件首行加上 SET NAMES utf8mb4;,才让中文乱码问题得到了彻底、干净的解决。