核心三点:连对本容器(统一用--host=localhost)、密码不被截断(优先-e MYSQL_PWD或mysql_config_editor)、文件直落宿主机(单引号包裹sh -c并重定向)。卡住或1045错误主因是docker exec默认无TTY导致mysqldump无法读取密码,尤其启用了secure_file_priv时。

直接用 docker exec 备份 MySQL 容器数据,核心就三点:连对本容器、密码不被截断、文件落盘在宿主机——做错任一环节,备份就静默失败或内容为空。
为什么 docker exec 执行 mysqldump 会卡住或报错 Access denied
常见现象是命令没报错但终端无输出,或直接提示 mysqldump: Got error: 1045: Access denied。根本原因不是密码错,而是 stdin 被关闭:docker exec 默认不分配伪终端(TTY),而 MySQL 客户端在非交互模式下会拒绝读取密码输入(尤其启用了 secure_file_priv 时)。
- 加
-t -i可临时解决:强制分配 TTY + 保持 stdin 打开,让mysqldump -p能等你手动输密码 - 但生产环境更推荐用
-e MYSQL_PWD=xxx配合--no-tty:避免终端依赖,也防止密码被 shell 提前解析(比如含@、/时) - 若已用
mysql_config_editor配置了登录路径,则-p和密码参数都可省略,-t -i也不需要
mysqldump --host 填 localhost 还是容器名?
填 localhost。这是最容易踩的坑:你在容器内执行 mysqldump,它默认连接的是「当前容器进程监听的 MySQL 实例」,即 127.0.0.1:3306 或 localhost:3306。填容器名(如 mysql-db)会让 DNS 解析走 Docker 网络,可能连到另一个容器,甚至因网络隔离失败。
- 确认 MySQL 在容器内监听
127.0.0.1:3306:进容器跑netstat -tlnp | grep :3306 - 如果
my.cnf里设了bind-address = 127.0.0.1,那--host=localhost是唯一可靠选项 -
--host=127.0.0.1和--host=localhost行为不同:前者强制走 TCP,后者可能走 Unix socket(取决于 MySQL 配置),日常统一用localhost更稳
如何确保备份文件写到宿主机,而不是容器里?
别用 docker exec ... > /tmp/backup.sql 再 docker cp 拷出来——多一步就多一个失败点,容器重启后 /tmp 里的文件就丢了。
- 正确做法是靠宿主机 shell 解析重定向:管道把 stdout 直接流到宿主机文件系统,容器内不落地
- 命令必须用单引号包裹整个
sh -c 'exec mysqldump ...',否则宿主机 shell 会提前解析$MYSQL_ROOT_PASSWORD变量 - 示例:
docker exec mysql-db sh -c 'exec mysqldump -u root -p\$MYSQL_ROOT_PASSWORD myapp' > backup_$(date +%Y%m%d).sql - 密码含特殊字符时,改用
-e MYSQL_PWD=xxx更安全:docker exec -e MYSQL_PWD=abc@123 mysql-db mysqldump -u root --no-tty myapp > backup.sql
真正麻烦的从来不是命令怎么写,而是备份时 MySQL 正在写 binlog、事务未提交、或 InnoDB buffer pool 有脏页——这些都会让 mysqldump 输出不一致。所以务必加 --single-transaction(InnoDB)或 --lock-all-tables(MyISAM),否则备份出来的 SQL 可能根本恢复不了。


















