Featured image of post MySQL Docker 实战:从 5.6 到 8.0 的全版本最佳实践

MySQL Docker 实战:从 5.6 到 8.0 的全版本最佳实践

官方镜像与 bitnami 镜像的 my.cnf 模板、备份/恢复/导入导出、参数调优(lower_case_table_names / sql_mode / 字符集)、Lost connection 排错——MySQL 5.6 到 8.0 一次性收齐

MySQL 5.6 / 5.7 / 8.0 三个版本在 Docker 里的差异比想象大:5.6 默认字符集 latin1、5.7 引入 caching_sha2_password、8.0 默认认证插件改了 native_password。配置文件模板、备份恢复命令、参数调优是日常最常碰到的三件事。这篇文章把 MySQL 在 Docker 里的实战(5.6/5.7/8.0、官方镜像 / bitnami 镜像、配置 / 备份 / 调优)一次性收齐。

阅读对象:日常用 MySQL 跑业务、需要在 Docker 里搭开发库、关心字符集和大小写敏感性、被 “Lost connection” / “group by” 报过错的后端 / DBA 同学。 覆盖范围:MySQL 5.6/5.7/8.0 官方镜像启动;bitnami/mysql:8.0 镜像(生产推荐);my.cnf 模板(utf8mb4 / lower_case_table_names=1 / 慢日志);备份/恢复/导入导出;only_full_group_by 解决;Lost connection 调优。

一、为什么要在 Docker 里跑 MySQL

场景价值
CI/CD 测试库每个 Pipeline 干净环境,跑完即弃
多版本并存5.6 旧系统 + 8.0 新系统一台机器
快速 demo一行 docker run 拉起完整 MySQL
跨平台开发Mac/Win/Linux 容器化一致
生产仍建议用云 RDS 或独立机器,Docker 化适合测试 / 演示

生产建议:核心业务用云厂商 RDS(高可用 / 备份 / 监控内置)。Docker 跑 MySQL 用于演示、CI、个人开发。


二、官方镜像启动

2.1 5.6 启动

1
2
3
4
5
6
7
docker run -d -p 3306:3306 --name mysql56 \
  -e MYSQL_ROOT_PASSWORD={{REDACTED}} \
  --restart=always \
  -v /etc/localtime:/etc/localtime:ro \
  --net mynetwork --ip 172.18.0.2 \
  --privileged=true \
  mysql:5.6.30

2.2 5.7 启动

1
2
3
4
5
6
7
docker run -d --name mysql57 \
  --restart=always \
  -p 3308:3306 \
  -e MYSQL_ROOT_PASSWORD=x5 \
  -v /home/old-<your-org>/mysql5.7/conf:/etc/mysql/conf.d \
  -v /home/old-<your-org>/mysql5.7/data:/var/lib/mysql \
  mysql:5.7.43

2.3 8.0 启动(推荐生产用 bitnami)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
docker run -d \
  --name mysql \
  --restart=always \
  --privileged=true \
  -p 3309:3306 \
  -e MYSQL_ROOT_PASSWORD={{REDACTED}} \
  -e TZ=Asia/Shanghai \
  -v /data/docker/mysql/db:/var/lib/mysql \
  -v /data/docker/mysql/conf:/etc/mysql/conf.d \
  mysql:8.0.23 \
  --default-authentication-plugin=mysql_native_password \
  --lower-case-table-names=1

Why --default-authentication-plugin=mysql_native_password MySQL 8.0 默认用 caching_sha2_password,老版本客户端(包括 Java 老驱动)连不上。显式指定 native_password 兼容性好。 Why --lower-case-table-names=1 Linux 默认 0(区分大小写),Windows 默认 1。开发环境跨平台会踩坑,统一设 1 更省事(注意:该参数不能在已有数据上改,需要初始就设)。

2.4 docker-compose 模板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
version: "3"
services:
  mysql:
    image: <your-public-ip>:13001/base/mysql:8.0.23
    container_name: mysql
    command: --default-authentication-plugin=mysql_native_password --lower-case-table-names=1
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: {{REDACTED}}
      TZ: Asia/Shanghai
    ports:
      - '3306:3306'
    security_opt:
      - seccomp:unconfined
    volumes:
      - "/data/mysql/db:/var/lib/mysql"
      - "/data/mysql/conf:/etc/mysql/conf.d"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-uroot", "-p{{REDACTED}}"]
      interval: 5s
      timeout: 10s
      retries: 10

三、bitnami/mysql 镜像(生产推荐)

bitnami/mysql 镜像比官方多了:非 root 进程运行、healthcheck、Tini init、健康检查脚本、Redis 风格的环境变量(MYSQL_ROOT_PASSWORD 等都大写)。生产环境推荐用。

3.1 拉取与重打 tag

1
2
3
4
docker pull bitnami/mysql:8.0.39-debian-12-r2
docker tag bitnami/mysql:8.0.39-debian-12-r2 \
  internal.example.com:13001/base/bitnami/mysql:8.0.39-debian-12-r2
docker push internal.example.com:13001/base/bitnami/mysql:8.0.39-debian-12-r2

3.2 my_custom.cnf(utf8mb4 + 大连接数)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
[mysqld]
skip-name-resolve
port = 3307
lower_case_table_names = 1
character-set-server = utf8mb4
secure-file-priv = NULL
default-time-zone = '+08:00'
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
group_concat_max_len = 4294967295
max_connections = 10000
max_user_connections = 5000
mysqlx_max_connections = 200
interactive_timeout = 28800
wait_timeout = 28800
thread_cache_size = 100
max_allowed_packet = 1G
net_buffer_length = 1M
log_bin_trust_function_creators = 1
binlog_rows_query_log_events = 1
slow_query_log = 1
long_query_time = 3
slow_query_log_file = slow.log

3.3 启动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
docker run -d \
  --net=host \
  --name mysql8 \
  --restart=always \
  -e MYSQL_ROOT_PASSWORD={{REDACTED}} \
  -e MYSQL_AUTHENTICATION_PLUGIN=mysql_native_password \
  -v /home/docker/mysql8/data:/bitnami/mysql/data \
  -e TZ=Asia/Shanghai \
  -v /home/docker/mysql8/conf/my_custom.cnf:/opt/bitnami/mysql/conf/my_custom.cnf:ro \
  internal.example.com:13001/base/bitnami/mysql:8.0.39-debian-12-r2

关键差异:bitnami 镜像的 datadir 是 /bitnami/mysql/data不是 官方的 /var/lib/mysql。备份恢复命令的路径要对应调整。

3.4 bitnami 容器的 mysql 命令路径

1
2
3
4
5
docker exec -it -u root mysql8 bash

# bitnami 路径
/opt/bitnami/mysql/bin/mysql
/opt/bitnami/mysql/bin/mysqldump

四、备份、恢复、导入

4.1 备份

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 官方镜像
docker exec mysql /usr/bin/mysqldump \
  -u root -p{{REDACTED}} \
  --single-transaction --quick \
  safety_manhole_cover > /data/mysql/bak/safety_manhole_cover.sql

# bitnami 镜像
docker exec mysql8 /opt/bitnami/mysql/bin/mysqldump \
  -u root -p{{REDACTED}} \
  --single-transaction --quick \
  hxl_industry_iot_dev > /home/iot.sql

--single-transaction:InnoDB 的事务一致快照,不影响业务--quick:逐行取数据,避免大表 dump 时 OOM。

4.2 恢复(先建库再导入)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 1. 建库
cat << 'EOF' | docker exec -i mysql /usr/bin/mysql -u root --password={{REDACTED}}
DROP DATABASE IF EXISTS `safety_manhole_cover`;
CREATE DATABASE `safety_manhole_cover` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
EOF

# 2. 导入
cat /data/mysql/bak/safety_manhole_cover.sql | \
  docker exec -i mysql /usr/bin/mysql \
  -u root --password={{REDACTED}} \
  safety_manhole_cover

4.3 备份脚本(保留 7 天)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
cat << 'EOF' > /data/mysql/bak.sh
#!/bin/bash
set -e
DATE=$(date +%Y%m%d%H%M%S)
BACK_DATA=iot-${DATE}.sql
BAK_PATH=/data/mysql/bak
mkdir -p ${BAK_PATH}
docker exec mysql /usr/bin/mysqldump \
  -u root -p{{REDACTED}} \
  --single-transaction --quick \
  hxl_chemistry_park_iot > ${BAK_PATH}/$BACK_DATA
find ${BAK_PATH} -name "*.sql" -mtime +7 -print0 | xargs -0 rm -rf
EOF
chmod +x /data/mysql/bak.sh

小坑:Windows 编辑的 .sh 脚本传到 Linux 报 /bin/bash^M: bad interpreter。vim 改文件类型:set ff=unix

4.4 K8s 中备份

1
2
3
kubectl exec -i "$(kubectl get pods -n <ns> | grep mysql | awk '{print $1}')" \
  -n <ns> -- bash -c \
  "/usr/bin/mysqldump -u root -p'password' <db_name>" > backup.sql

五、常见调优

5.1 解决 only_full_group_by 报错

报错信息:

1
2
which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by

临时解决(重启失效):

1
2
SELECT @@sql_mode;
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';

永久解决:在 my.cnf[mysqld] 下加:

1
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

5.2 解决 Lost connection to MySQL server during query

通常是大表 dump / 慢查询超过 net_read_timeout

1
2
3
4
show global variables like '%timeout%';

set global net_read_timeout = 120;
set global net_write_timeout = 900;

5.3 大小写不敏感

1
2
3
show global variables like '%lower_case%';
-- 0: 大小写敏感(Linux 默认)
-- 1: 大小写不敏感(Windows 默认)

在 my.cnf 加:

1
lower_case_table_names = 1

重要:该参数只能在初始化时设,已有数据后改会导致表找不到。

5.4 用户自定义函数 UDF

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 1. 查询插件地址
docker exec -it mysql /bin/bash
mysql -uroot -p
show variables like 'plugin_dir';
-- /usr/lib/mysql/plugin

# 2. 复制 UDF 文件
docker cp ~/libHttpClient.so mysql:/usr/lib/mysql/plugin

# 3. 创建函数
create function http_get RETURNS string soname 'libHttpClient.so';
create function http_post RETURNS string soname 'libHttpClient.so';

# 4. 重启容器生效
docker restart mysql

六、MySQL 5.6 / 5.7 / 8.0 选型速查

维度5.65.78.0
发布时间201320152018
默认字符集latin1utf8mb4utf8mb4
默认认证mysql_native_passwordmysql_native_passwordcaching_sha2_password
JSON 支持弱(需用 TEXT)原生 JSON 类型强(JSON_TABLE 函数)
窗口函数
通用表表达式 (CTE)
推荐场景遗留系统中间状态(推荐升级到 8.0)新项目默认
Docker 官方支持

经验法则

  • 新项目:MySQL 8.0(JSON、窗口函数、CTE 都要)
  • 老系统:先看能不能升级到 8.0;不能的话 5.7(5.6 已 EOL)
  • 测试/演示:用 bitnami/mysql:8.0,省事

七、扩展阅读

使用 Hugo 构建
主题 StackJimmy 设计