<p>MySQL 8.4 多实例不能靠反复 docker run 同一镜像加不同端口实现,必须严格隔离 data 目录、server-id、配置文件挂载路径及启动参数,且 MYSQL_* 环境变量仅首次初始化有效。</p>

直接上结论:MySQL 8.4 多实例不能靠反复 docker run 启同一个镜像加不同端口来实现——数据目录冲突、--lower-case-table-names 参数无法重复生效、环境变量只在首次初始化时起作用,硬套单实例写法必踩坑。
docker-compose.yml 中定义多个 MySQL 8.4 服务时,必须隔离 data 目录和 server-id
MySQL 主从、双主或多节点部署,本质是多个独立实例。每个实例必须满足:
-
/var/lib/mysql挂载路径绝对不能共用(哪怕加了:z或:Z);否则第二个实例启动失败,日志卡在InnoDB: Operating system error number 22 - 每个实例的
server-id必须唯一(主库设1,从库分别设2、3),且必须通过配置文件或command传入,不能靠环境变量 - 若用自定义
my.cnf,必须放在挂载到/etc/mysql/conf.d/下(不是/etc/mysql/my.cnf),否则被忽略 - 所有实例都需显式声明
--lower-case-table-names=1(注意是连字符!),且必须出现在command中、镜像名之后
MYSQL_* 环境变量对多实例几乎无效,初始化逻辑只跑一次
你给 mysql-master 设了 MYSQL_DATABASE=master_db,又给 mysql-slave1 设了 MYSQL_DATABASE=slave_db?没用。因为:
-
MYSQL_ROOT_PASSWORD是唯一强制变量,其他如MYSQL_DATABASE、MYSQL_USER只在对应容器的/var/lib/mysql为空时执行一次建库/建用户 - 一旦某个实例完成初始化(即
./data/master非空),再重启或重部署该服务,所有MYSQL_*变量全被忽略 - 多实例场景下,你根本没法保证谁先启动、谁后初始化,更不能指望靠环境变量自动创建不同库——得进容器手动执行
CREATE DATABASE和CREATE USER
挂载目录权限和 SELinux 是 Linux 上多实例启动失败最常见原因
在 CentOS/RHEL 或启用了 SELinux 的系统上,即使 docker-compose up 命令没报错,容器也可能静默退出或卡在 “Initializing database”:
- MySQL 容器内以 UID
999(mysql 用户)运行,宿主机挂载目录必须允许该 UID 写入:chown -R 999:999 ./data/master ./data/slave1 - SELinux 默认拦截容器对挂载目录的写入,必须加
:z标签:- ./data/master:/var/lib/mysql:z,否则日志里看不到明确错误,只看到反复重试 - ARM 架构(如 Apple Silicon、云厂商 ARM 实例)无需额外处理,但 x86_64 + CentOS 7/8/9 组合下,这个坑几乎必现
时区、字符集、默认时间戳行为必须每个实例单独配
TZ=Asia/Shanghai 只改容器系统时间,MySQL 服务本身仍用 UTC;character-set-server=utf8mb4 不写进 command 或 my.cnf,就还是 utf8mb3,存 emoji 会报错:
- 正确做法:每个服务的
command都带上完整参数,例如:["--default-time-zone='+08:00'", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_0900_ai_ci", "--lower-case-table-names=1"] - 不要图省事把参数写进共享的
my.cnf再挂载到所有实例——除非你确认所有实例需要完全一致的配置(比如全是只读从库) - MySQL 8.4 已移除
init_connect对SET NAMES的隐式支持,客户端连接时必须显式执行SET NAMES utf8mb4,否则中文乱码
真正麻烦的不是写配置,而是每次删容器前必须确认 ./data/* 是否清空;一旦残留,后续任何 MYSQL_* 设置都形同虚设。多实例下这个检查动作要翻倍做。


















