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.6 | 5.7 | 8.0 |
|---|
| 发布时间 | 2013 | 2015 | 2018 |
| 默认字符集 | latin1 | utf8mb4 | utf8mb4 |
| 默认认证 | mysql_native_password | mysql_native_password | caching_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,省事
七、扩展阅读