Runtime.exec()备份MySQL数据库的问题


前几天在用Runtime搞数据库备份,其实也就是使用Runtime.exec()调用mysqldump的指令来备份数据库,由于没怎么接触过Runtime这个类,遇到了一些问题,头发差点都给我撸秃了,先是waitFor(),然后是备份数据库,文件出来了,但是是空文件。

关于waitFor()堵塞

/*Win下,/C指执行字符串指令然后终止,属于cmd的属性,在cmd.exe中,输入cmd/?可以查看*/
String[] command = {"cmd","/C","某个cmd指令"};
/*Linux下*/
String[] command1 = {"/bin/sh","-c","某个linux指令"};
/*三个参数,cmd指令、环境变量、启动在哪个目录(不指定则在当前目录启动cmd.exe)*/
Process p = Runtime.getRuntime.exec(command,null,null);
p.waitFor();

上面的command是指创建子进程,执行start后面的cmd指令,并且打开cmd.exe。
上面这个就是创建子进程并执行某个命令

waitFor()堵塞是因为,子进程创建时,会伴随着输入、输出、错误三种流的出现,输出倒是没什么,但是如果你的指令会有大量输入,即cmd中的输出,相对于java程序来说是输入,那你输入缓冲区读了一堆乱七八糟的东西,没人来给它排,那自然就堵在那儿,出也没地方出,进也没地方进了。
所以就需要创建地方,来专门容纳它们

String line = null;
StringBuilder inmessage = new StringBuilder();
InputStreamReader in = null;
BufferedReader br = null;
//创建输入流,以防止输入流缓冲区满了后导致process堵塞
try {
	/*用process.getInputStream()获取输入流,返回值是InputStream类型*/				
	in = new InputStreamReader(process.getInputStream());
	br = new BufferedReader(in);
	/*读出*/
	while((line = br.readLine()) != null) {
		inmessage.append(line);
	}
	} catch (IOException e) {
		e.printStackTrace();
	}finally{/*关闭流*/
		if(in != null){
			try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		if(br != null){
			try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

错误流和它输入流差不多,代码都是一样的,只不过获取是

process.getErrorStream();

关于备份数据库为空的问题

因为对于Runtime这个类不熟悉,所以我在这个问题上纠结了很久很久,上网查了一下大家的出现问题的地方,有些因为空格问题,有些因为这个程序的运行环境问题等等

首先我的大概代码(简化)就是

String[] command = {"cmd","/C","start mysqldump -uroot -p123456 kdwl > D:\\kdwl.sql"};
Process p = Runtime.getRuntime.exec(command,null,null);
p.waitFor();

问题1

后面推测可能是由于start命令导致的备份出的数据为0KB,具体原由未知

解决方案

把指令写进.bat批处理文件,然后再执行批处理文件即可
大致代码就是

String[] command = {"mysqldump.bat"};
Process p = Runtime.getRuntime.exec(command,null,null);
p.waitFor();

这样就可以成功备份,并且不会有空的备份文件了

或者

/*删除start*/
String[] command = {"cmd","/C","mysqldump -uroot -p123456 kdwl > D:\\kdwl.sql"};
Process p = Runtime.getRuntime.exec(command,null,null);
p.waitFor();

或者

/*为start加上/B属性,/B启动应用程序,但不创建新窗口*/
String[] command = {"cmd","/C","start /B mysqldump -uroot -p123456 kdwl > D:\\kdwl.sql"};
Process p = Runtime.getRuntime.exec(command,null,null);
p.waitFor();

我的疑惑
我的waitFor()是不堵塞的,并且显示成功运行,确实也在这个D盘创建了个kdwl.sql的文件,但是就是空的,为此我还去查了MySQL日志,与正常备份的作比较,不过就是摸不着头脑,因为对于MySQL来说,这个并没有出现在错误日志里,而是正常出现在了日志里。

2020-04-02T08:16:40.572694Z	   67 Query	SET SESSION character_set_results = 'utf8mb4'
2020-04-02T08:16:40.577407Z	   67 Quit	

上面没贴出来,上面与正常成功的备份没有什么不同,就像是运行到这步突然断掉了联系

然后我将start删去之后,发现了指令可以成功备份了,并且不是空文件,也就是

String[] command = {"cmd","/C","mysqldump -uroot -p123456 kdwl > D:\\kdwl.sql"};
Process p = Runtime.getRuntime.exec(command,null,null);
p.waitFor();

然后我去查了一下start指令的解释
启动一个单独的窗口以运行指定的程序或命令。

这是cmd中给出的解释
关于这个指令start 备份数据库,在cmd.exe中运行也是输出一个空sql文件

我的猜测
这个start是开启一个单独的窗口运行指定的程序或命令,但是mysqldump.exe本身运行后就会闪退,我们一般都是在cmd或是其他工具中备份的,所以导致了命令本身没有被执行完,然后mysqldump.exe一闪而过,备份也没备份的完,就导致了0KB的情况,我也试过了,如果在执行备份后,快速关闭cmd.exe也确实会有1KB等现象出现。

问题2

我搜索出来的很多人是因为路径问题

解决方案

一般来说不会有这个问题,因为大家在Windows上用MySQL的时候都会在环境变量里配置bin的路径,然后运行mysqldump时,会自动去找。

检查你的mysqldump.exe的路径是否有空格,或者是否正确

总结

1. waitFor()阻塞问题

加入两个输入流,用来输入你的指令带出的一堆输出物

2. 备份的数据库为空问题,

使用批处理文件储存命令,然后调用批处理文件,
如果用了start命令,删去start即可,当然加上/B属性也可以(具体原因未知),
mysqldump.exe路径有问题,或是有空格
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

这可怎么整啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值