MySQL 8.4 容器启动时,环境变量仅在空数据目录初始化阶段生效;MYSQL_ROOT_PASSWORD为唯一强制变量,其余如MYSQL_DATABASE、MYSQL_USER等均属一次性指令,初始化后即失效,且lower_case_table_names、时区、字符集等必须通过command参数或my.cnf显式配置,不可依赖环境变量。

MySQL 8.4 容器启动时,环境变量只在首次初始化阶段生效;一旦数据目录 /var/lib/mysql 非空,所有 MYSQL_* 变量都会被忽略——这是绝大多数人连不上库、找不到库、用户没创建成功的根本原因。
MYSQL_ROOT_PASSWORD 是唯一强制要求的环境变量
没有它,容器会直接退出并报错 error: database is uninitialized and password option is not specified。其他变量全属“一次性初始化指令”,仅作用于空数据目录场景:
-
MYSQL_DATABASE=app_db:仅当/var/lib/mysql为空时,自动执行CREATE DATABASE app_db -
MYSQL_USER=app_user+MYSQL_PASSWORD=app_pass:仅在设置了MYSQL_DATABASE的前提下,才为该库创建用户并授ALL PRIVILEGES ON app_db.* -
MYSQL_ALLOW_EMPTY_PASSWORD=yes:跳过密码检查(测试专用,生产禁用) -
MYSQL_RANDOM_ROOT_PASSWORD=yes:不设密码,启动后必须用docker logs mysql84查看生成的随机密码
lower_case_table_names=1 必须在初始化时就定死
MySQL 8.4 不允许运行时修改 lower_case_table_names,且该参数必须在第一次初始化数据库时通过命令行传入,不能靠 my.cnf 后期覆盖。否则容器会卡在启动状态,日志里反复出现 InnoDB: Operating system error number 22。
正确做法是在 docker-compose.yml 的 command 中显式声明:
command: ["--lower-case-table-names=1", "--character-set-server=utf8mb4"]
注意两点:
- 参数名必须是
--lower-case-table-names(带连字符),不是下划线;MySQL 8.4 已弃用lower_case_table_names形式 - 必须放在镜像名之后、
docker run命令参数位置,写在environment或my.cnf里完全无效
时区和字符集不能只靠环境变量解决
TZ=Asia/Shanghai 确实能修正容器内系统时间,但 MySQL 服务本身仍可能用 UTC —— 因为 MySQL 启动时不读取系统 TZ,需额外指定:
- 用
command加--default-time-zone='+08:00' - 或在挂载的
my.cnf中写入[mysqld] default-time-zone = '+08:00'
字符集同理:MYSQL_ROOT_PASSWORD 不影响服务端默认字符集。必须显式设置 --character-set-server=utf8mb4 和 --collation-server=utf8mb4_0900_ai_ci(MySQL 8.4 默认排序规则已变,别再套用 8.0 的 utf8mb4_unicode_ci)。
初始化完成后,环境变量就彻底失效了
删掉容器、重建、但没清空 ./data 目录?那 MYSQL_DATABASE 和 MYSQL_USER 就再也不起作用。此时若想新增库或用户,只能进容器手动执行:
docker exec -it mysql84 mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS newdb;"
或者用初始化脚本(挂载 /docker-entrypoint-initdb.d/ 下的 .sql 或 .sh 文件),但要注意:这些脚本也只在数据目录为空时运行一次。
真正容易被忽略的是:MySQL 8.4 的初始化逻辑比 8.0 更严格,任何配置冲突(比如 my.cnf 里写了 skip-networking,又没配 bind-address)都会导致服务静默失败——查 docker logs mysql84 时得重点盯 mysqld: ready for connections 这一行是否出现。


















