runtime.exec()调用mysqldump卡住的根本原因是未读取stderr导致子进程阻塞;必须双线程分别读取stdout和stderr;凭据应通过权限为600的--defaults-file传递,禁止明文拼接;备份需流式写入防oom;恢复时需注意path、权限、字符集及退出码语义。

Runtime.exec() 调用 mysqldump 为什么总卡住或没输出
根本原因不是命令写错了,而是 mysqldump 的标准错误流(stderr)未读取,导致子进程阻塞 —— 尤其在密码为空、权限不足或网络超时等场景下,错误信息堆在缓冲区,Process.waitFor() 就永远等不到结束。
- 必须同时启动两个线程:一个读
process.getInputStream()(stdout),一个读process.getErrorStream()(stderr),否则大概率卡死 - 不要用
process.getOutputStream()向 mysqldump 写密码(已过时且不安全),改用--defaults-file或环境变量方式传凭据 - Windows 下注意路径分隔符和 shell 解析差异,建议统一用
new String[]{"cmd", "/c", "mysqldump", ...}包一层
如何安全传递 MySQL 凭据而不暴露在进程列表里
直接拼接 -u user -p'pass' 是高危操作:密码会出现在 ps aux 或 jstack 输出中,任何有权限查进程的用户都能看到。
- 推荐方案:用
--defaults-file=/path/to/my.cnf,其中/path/to/my.cnf权限设为600(仅属主可读写),内容为:[client] host=localhost user=backup_user password=xxx port=3306
- Java 中生成临时 my.cnf 文件后,务必在 dump 完成后立即
Files.deleteIfExists(Paths.get(tempCnfPath)) - 避免用
System.setProperty("MYSQL_PWD", "..."):某些 MySQL 版本不认,且 JVM 参数可能被其他组件误读
备份文件流式写入与大库防 OOM 的关键控制点
直接把整个 dump 结果读进 String 或 byte[],10GB 库瞬间撑爆堆内存。必须边读边写,且控制缓冲区大小。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 用
BufferedInputStream包装process.getInputStream(),每次read(byte[], 0, 8192)写入FileOutputStream - 别忽略
read()返回值:返回-1表示流结束,返回0不代表结束,要继续读 - 设置
mysqldump --single-transaction --skip-lock-tables减少锁表时间,但注意:该组合对 MyISAM 无效,备份一致性需自行评估
恢复时 mysql 命令执行失败的典型信号与修复
常见现象是 Java 端返回码为 1,但没报错信息,或日志里只显示 “mysql: command not found” —— 实际上是环境变量没继承过去。
-
Runtime.exec(String[])不自动加载 shell 配置(如 ~/.bashrc),mysql可能不在 PATH 里;显式指定绝对路径,比如/usr/bin/mysql或C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe - 恢复 SQL 文件时,如果含
CREATE DATABASE,确保目标用户有CREATE权限;否则加--force并捕获 stderr 中的 “ERROR 1044” - 注意字符集:备份时若用了
--default-character-set=utf8mb4,恢复时也得加同样参数,否则中文可能变 ?
最易被忽略的是 mysqldump 进程的退出码含义:0 是成功,1 是语法/连接错误,2 是 IO 错误(如磁盘满),但 Java 里只看 process.exitValue() 不够,必须结合 stderr 内容判断真实失败原因 —— 很多“备份成功了但文件是空的”问题,其实 exitValue 是 2,却被当成 0 处理了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










