必须通过 volumes 显式挂载 init.sql 到 /docker-entrypoint-initdb.d/init.sql,mysql 官方镜像仅在此目录下且仅首次启动时执行 sql 脚本;挂载到其他路径或使用 command/init-file 均无效。

docker-compose.yml里怎么挂载/init.sql到/docker-entrypoint-initdb.d/
必须用 volumes 显式挂载,不能靠 command 或 init-file 参数。MySQL 官方镜像只认这个目录下的文件,且只在首次启动时执行。
常见错误是把 SQL 文件挂载到容器内其他路径(比如 /sql 或 /data),结果脚本完全不运行。正确写法示例如下:
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: my-secret-pw
MYSQL_DATABASE: app_db
volumes:
- ./mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
-
./init.sql是宿主机上的文件路径,确保它存在且可读 - 挂载目标必须是
/docker-entrypoint-initdb.d/init.sql,不能是子目录如/docker-entrypoint-initdb.d/sql/init.sql - 如果要挂多个脚本,推荐统一放在本地
./init/目录下,然后整目录挂载:./init:/docker-entrypoint-initdb.d
SQL脚本执行失败的三个典型现象
脚本看似挂载成功,但数据库没建表、没插数据,日志里也无报错——这往往不是语法问题,而是执行条件没满足。
-
MySQL container is already initialized:数据卷./mysql-data非空,容器认为已初始化,直接跳过脚本。解决方法是删掉该目录或加-v清理:docker-compose down -v - 脚本执行一半中断,日志出现
ERROR 1045 (28000): Access denied:说明脚本里用了非 root 用户连接,而初始化阶段只允许mysql -uroot -p"$MYSQL_ROOT_PASSWORD"方式执行 - 建库语句没生效,但
app_db库却自动创建了:因为MYSQL_DATABASE=app_db环境变量会先于脚本执行,你脚本里的CREATE DATABASE IF NOT EXISTS app_db实际是冗余的
.sh 脚本和 .sql 脚本的执行差异
两者都能用,但行为不同:.sql 直接进 MySQL client 执行;.sh 则在容器 shell 环境中运行,可以做判断、调用命令、甚至 curl 外部服务。
-
.sql文件默认以root身份执行在MYSQL_DATABASE指定的库中,无需写USE app_db -
.sh文件需要自己拼接 mysql 命令,例如:mysql -uroot -p"$MYSQL_ROOT_PASSWORD" "$MYSQL_DATABASE" - 注意权限:.sh 文件在宿主机上需有可执行位(
chmod +x init.sh),否则容器内执行时报Permission denied - 字母序决定执行顺序:
01-schema.sql一定早于02-data.sql,这对外键依赖很关键
为什么 init.sql 有时只执行一次,有时每次重启都跑
核心判断依据只有一个:/var/lib/mysql 目录是否为空。只要这个 volume 挂载点有文件(哪怕只有 ibdata1),后续所有重启都不会再触发 /docker-entrypoint-initdb.d/ 下的脚本。
- 误操作:用
docker-compose up --force-recreate重建容器,但没清 volume,脚本不会重跑 - CI/CD 场景常见坑:测试环境反复部署,忘了清理
mysql-data,导致每次都是“已初始化”状态 - 调试技巧:启动前临时加一句
ls -la ./mysql-data,确认目录为空;或改用内存 volume 测试:./mysql-data:/var/lib/mysql:rw,tmpfs(仅限开发)
最易被忽略的一点:挂载点路径写错(比如少了个点变成 mysql-data:/var/lib/mysql),Docker 会静默创建匿名 volume,导致你以为清空了目录,其实数据还在别处。











