Docker Compose:MySQL 从绑定目录改为命名卷后「数据不见了」?迁移与选型说明
适用场景:原先使用
./data/mysql:/var/lib/mysql持久化,后来改为mysql-data:/var/lib/mysql等命名卷,发现库里没有以前的库表。本文说明原因,并给出两种通用解决方案(迁移进卷 / 改回绑定目录),可直接用于团队文档或技术博文。
一、现象
- 修改
docker-compose.yml后,MySQL 能正常启动。 - 但登录后发现:以前的数据库、表都不见了,像刚装好的新实例。
- 宿主机上
./data/mysql(或你原来的目录)里明明还有一堆文件。
二、原因(核心一句话)
绑定挂载(bind mount)和命名卷(named volume)是两套不同的存储位置。
改配置后,新容器读写的是新位置 (例如空的 mysql-data 卷),不会自动 去用你旧的 ./data/mysql,所以旧数据「看起来丢了」,其实仍在原目录里。
| 挂载方式 | 数据实际在哪 |
|---|---|
./data/mysql:/var/lib/mysql |
宿主机你指定的目录(如项目下 data/mysql) |
mysql-data:/var/lib/mysql |
Docker 管理的卷(docker volume ls 里能看到,路径由 Docker 决定) |
MySQL 官方镜像只在数据目录为空 时执行 docker-entrypoint-initdb.d 里的初始化脚本;若命名卷是新的空卷,还会再跑一遍初始化,进一步让你感觉「全是新的」。
三、方案 A:把旧数据迁移进命名卷(推荐继续用命名卷时)
目标 :保留 mysql-data:/var/lib/mysql 的配置,把原 ./data/mysql 里的内容拷进该卷。
前置条件
- MySQL 已停止(避免拷贝过程中文件被写坏)。
- 旧数据与当前镜像 MySQL 大版本一致(例如都是 8.0),跨大版本直接拷数据目录风险高,应用备份/逻辑导出迁移。
- 若命名卷里已经跑过一次 MySQL、生成了新数据,且你要以旧数据为准,通常需要先处理空卷(见下)。
操作步骤(Linux / macOS / Git Bash)
在项目根目录(docker-compose.yml 所在目录)执行:
bash
# 1. 停止 MySQL
docker compose stop mysql
# 2. 查看卷名(Compose 会加项目前缀,常见为 目录名_mysql-data)
docker volume ls
# 3. 若确定要用旧数据完全覆盖卷内内容,且卷可删(注意:会删掉卷里当前所有内容)
# docker volume rm <项目前缀>_mysql-data
# 若删不掉,先:docker compose rm -f mysql
# 4. 将宿主机旧数据目录拷入命名卷(把 VOLUME_NAME 换成上一步看到的全名)
docker run --rm \
-v VOLUME_NAME:/to \
-v "$(pwd)/data/mysql:/from:ro" \
alpine sh -c "cp -a /from/. /to/"
# 5. 启动 MySQL
docker compose up -d mysql
Windows PowerShell(Docker Desktop)
在项目根目录 执行,VOLUME_NAME 替换为 docker volume ls 中的实际名称(例如 devops-core_mysql-data):
powershell
docker compose stop mysql
docker volume ls
docker run --rm `
-v "VOLUME_NAME:/to" `
-v "${PWD}/data/mysql:/from:ro" `
alpine sh -c "cp -a /from/. /to/"
docker compose up -d mysql
验证
bash
docker compose exec mysql mysql -u root -p -e "SHOW DATABASES;"
应能看到迁移前的库名。
注意事项
- 拷贝前尽量保证旧库是正常关闭后的数据目录;异常宕机后的目录可能需要 InnoDB 恢复,以日志为准。
- 不要把不同机器、不同小版本混用 不经测试;生产环境更建议:mysqldump 逻辑备份 → 新实例导入。
四、方案 B:改回绑定目录(零迁移,立刻用回旧数据)
目标 :不再使用命名卷,继续用 ./data/mysql:/var/lib/mysql。
在 docker-compose.yml 中把:
yaml
volumes:
- mysql-data:/var/lib/mysql
改回:
yaml
volumes:
- ./data/mysql:/var/lib/mysql
并在文件底部 volumes: 声明块 里删除不再使用的 mysql-data:(若没有其他服务引用)。
然后:
bash
docker compose up -d mysql
数据会立刻从原 ./data/mysql 读取,无需拷贝。
注意 :之后请始终在同一项目根目录 执行 docker compose,避免相对路径 ./data/mysql 指向别的位置。
五、选型建议(方便写进团队规范)
| 需求 | 更合适的做法 |
|---|---|
| 数据要跟项目目录放一起、方便备份/拷贝 | 绑定挂载 ./data/mysql |
| 避免「换目录执行 compose 导致换了一套数据」、与平台无关 | 命名卷 mysql-data |
| 已用命名卷但历史数据在旧目录 | 按方案 A 迁移,或方案 B改回 |
六、一句话总结
改挂载 = 换磁盘;旧数据不会自动跟过去。
要么把旧目录拷进新卷 (方案 A),要么继续挂载旧目录(方案 B)。
版权声明与转载
本文为通用运维说明,可根据需要修改示例路径、卷名后用于内部 Wiki 或公开技术博客;转载时注明出处即可。