Docker Mysql DB 初始化 UTF8 乱码问题的解决

最近在补单元测试,需要 mock 一个 DB,初始化 dump 数据阶段需要 dump 一些带中文的数据,但是发现跑起来都是乱码,非常迷惑,因为我们在链接、建立 Table 阶段都约定了:

# 连接串中
charset=utf8mb4,utf8
# 创建 DB
CREATE TABLE `lang` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

在创建完的 DB 里执行插入也是一样,没有乱码。因此问题肯定出在了 dump 阶段,和我们的标准插入是有一定差异的,首先我们来看一下 docker-compose.yaml 的写法:

version: "3.7"
services:
  db:
    image: mysql:5.6
    ports:
    - 13306:3306
    environment:
    - MYSQL_ROOT_PASSWORD=root
    - TZ=Asia/Shanghai
    volumes:
    - .:/docker-entrypoint-initdb.d
    command: [
      '--character-set-server=utf8mb4',
      '--collation-server=utf8mb4_general_ci'
    ]
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "--protocol=tcp"]
      timeout: 20s
      interval: 1s
      retries: 20

在命令中我们可以看到我们也规定了编码,但结果还是会出现乱码。

回到 dump 中,Docker 中的 DB 初始化是从 docker-entrypoint-initdb.d 出发进行 mysql-dump 的(见 https://hub.docker.com/_/mysql - Initializing a fresh instance 一节)

When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.

之后我们好不容易查到了对应的 issue:https://github.com/docker-library/mysql/issues/131

the issue is not MySQL, but the moment the terminal (shell) imports the SQL statements. One simple proof (in this case for MariaDB, but same for MySQL)

因此我们其实应该设置的是 terminal 对应的环境变量 LANG=C.UTF-8,docker compose 变成了:

version: "3.7"
services:
  db:
    image: mysql:5.6
    ports:
    - 13306:3306
    environment:
    - MYSQL_ROOT_PASSWORD=root
    - TZ=Asia/Shanghai
    - LANG=C.UTF-8
    volumes:
    - .:/docker-entrypoint-initdb.d
    command: [
      '--character-set-server=utf8mb4',
      '--collation-server=utf8mb4_general_ci'
    ]
    healthcheck:
      test: ["CMD", "mysqladmin" ,"ping", "--protocol=tcp"]
      timeout: 20s
      interval: 1s
      retries: 20

之后 dump 果然完事儿了。

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: mysql, docker

添加新评论