核心思路是通过SSH在远程服务器本地执行mysqldump并拉回结果,而非本地直连远端MySQL;需密钥免密登录、统一绝对路径、用parallel并发、捕获stderr与退出码验证成败、边dump边gzip压缩。
用 mysqldump + SSH 实现跨服务器并行导出
核心思路不是写一个“万能接口”,而是把备份动作拆解成可远程触发、可统一收口的命令组合。每台服务器只需开放 ssh(密钥认证),无需额外部署 api 服务或数据库中间件。
常见错误是直接在本地拼接 mysqldump 命令试图连接远端 MySQL,结果因网络策略或权限限制失败;正确做法是通过 ssh 登录目标服务器,在其本地执行 mysqldump,再把文件拉回本地。
- 必须使用 SSH 密钥免密登录,密码交互会阻断批量流程
- 所有服务器的 MySQL 用户需有对应库的
SELECT和LOCK TABLES权限(或用--single-transaction避免锁表) - 导出路径建议统一用绝对路径,例如
/backup/db_$(date +\%Y\%m\%d).sql,避免因工作目录不同导致文件丢失 - 加
--skip-lock-tables或--single-transaction(仅 InnoDB)防止长事务阻塞其他业务
用 parallel 或 GNU parallel 并发执行多机备份
串行逐台备份耗时太长,parallel 是最轻量、最可控的并发方案。它不依赖 Python/Ansible 等运行时,只要目标服务器装了 ssh 和 mysqldump 就能跑。
示例命令(备份 3 台服务器的 app_db 库):
echo "server1 server2 server3" | tr ' ' '\n' | parallel -j3 'ssh {} "mysqldump -u backup_user -p\"password\" --single-transaction app_db" > ./backups/{}_app_db_$(date +%Y%m%d).sql'
注意点:
-
-j3表示最多同时跑 3 个任务,避免压垮跳板机或目标服务器 CPU/IO - 密码明文写在命令里不安全,应改用
mysql_config_editor配置登录简档,或用ssh转发 agent - 若某台服务器备份失败,
parallel默认继续执行其余任务,需配合--halt now,fail=1控制失败行为 - 输出文件名含服务器名和日期,避免覆盖,也方便后续按主机归档
备份失败时如何快速定位:看 stderr 和退出码
批量脚本静默失败是最头疼的问题。不能只靠文件是否存在判断成功——mysqldump 即使报错也可能生成空文件或部分数据。
关键检查项:
- 捕获
ssh的退出码:ssh server "cmd"成功返回 0,连接失败或命令崩溃返回非 0 - 强制重定向
stderr到独立日志,例如2>./logs/server1_$(date +%Y%m%d).err - 典型错误信息如
mysqldump: Got error: 1045: Access denied for user、ssh: connect to host xxx port 22: Connection timed out,都必须出现在日志里 - 加
--verbose参数给mysqldump,能看到实际连接的 host/port/user,排除配置错写成本地地址等问题
导出后压缩与清理:别让磁盘被 .sql 塞爆
纯 SQL 文件体积大,尤其带 BLOB 或历史数据的库。不压缩直接存档,几天就占满备份盘。
实操建议:
- 在远程服务器上用
gzip边 dump 边压缩:ssh server "mysqldump ... | gzip -c" > backup.sql.gz,减少网络传输量和本地磁盘压力 - 本地保存后,自动删除 7 天前的
.sql.gz文件:find ./backups -name "*.sql.gz" -mtime +7 -delete - 不要用
zip—— 它不支持流式处理,必须先落地再打包,多一次 IO;gzip和zstd支持管道直压 - 如果数据库超 10GB,考虑分库或加
--skip-extended-insert减少单行长度,便于后续 diff 或部分恢复
真正麻烦的是权限和路径不一致:同一段脚本在 A 服务器能写入 /backup/,到 B 服务器可能因 SELinux 或挂载选项被拒绝。每次新增机器,先手动跑通一遍完整链路,比事后查日志快得多。










