应直接调用mysqldump/mysql命令而非ScriptUtils,因其绕过JDBC瓶颈但需手动管理进程生命周期、错误码、超时及流捕获;ScriptUtils会OOM且不支持会话级开关、无进程状态感知。

Process 组件直接调用 mysqldump 或 mysql 命令做备份/恢复,本质是绕过 JDBC 的 I/O 瓶颈,但必须手动处理进程生命周期、错误码、超时和输出流捕获——否则脚本卡死、失败静默、大文件崩溃都是常态。
为什么不用 ScriptUtils.executeSqlScript 执行大 SQL 文件
Spring 的 ScriptUtils.executeSqlScript 会把整个 SQL 文件读进内存再逐行解析执行。对几十 GB 的还原文件来说:
- Java 堆内存必然 OOM,哪怕配到 8GB 也扛不住 20G+ 的
.sql文件 - 不支持
SET FOREIGN_KEY_CHECKS=0等会话级开关的自动注入,容易因外键约束报错中断 - 无法感知底层 MySQL 进程是否卡住、是否被 kill、是否写磁盘失败(比如磁盘满)
- 错误信息全丢在标准错误流里,不显式重定向就看不到具体哪一行出错
ProcessBuilder 启动命令的关键参数组合
用 ProcessBuilder 调用 mysqldump 或 mysql,不是简单拼字符串,得控制三类行为:
-
环境隔离:显式设置
environment().put("MYSQL_PWD", password),避免密码明文出现在ps aux输出中 -
输入/输出重定向:对备份用
redirectOutput(file);对恢复用redirectInput(file),并务必加redirectErrorStream(true)合并 stderr 到 stdout 方便捕获 -
超时与信号:不能只靠
process.waitFor()等到底,要配合process.waitFor(30, TimeUnit.MINUTES),超时后主动process.destroyForcibly(),否则僵尸进程堆积
异步执行多个数据库备份/恢复时的资源踩坑点
并发起多个 ProcessBuilder 进程看似能提速,但实际容易翻车:
- MySQL 服务端连接数限制(
max_connections)会被快速打满,新进程连不上就卡在 handshake 阶段 - 磁盘 I/O 成瓶颈:多个
mysqldump同时读表 + 多个mysql同时写入,顺序读写变随机,吞吐反而下降 40%+ - 没有共享的进度反馈机制,你看到“4 个 CompletableFuture 完成”,不代表数据已刷盘——
mysql进程可能还在 buffer 里攒批提交 - 建议上限设为 2–3 个并发,且每个任务启动前检查
SHOW STATUS LIKE 'Threads_connected',超过阈值就 sleep 回退
恢复失败后如何快速定位是命令问题还是数据问题
别急着重跑,先看三处输出:
- 检查
process.exitValue():0 是成功;1 通常是语法或权限错;2 是连接失败;其他值查 MySQL 官方文档 exit code 表 - 抓取合并后的输出流(含 stderr):重点搜
ERROR 1062(主键冲突)、ERROR 1215(外键缺失)、Got timeout reading communication packets(网络中断) - 确认目标库的
information_schema.TABLES行数是否匹配源库:如果mysql进程退出码是 0 但表记录数差很多,大概率是 SQL 文件里有USE xxx错位或事务没提交
真正难的不是启动进程,而是让进程在各种异常路径下都能留下可追溯的上下文——退出码、截断日志、临时文件残留、MySQL 线程状态快照,缺一不可。


















