java项目环境
SpringBoot+MybatisPlus
MySql 5.6+
备份需求
在java中使用mysqldump命令对指定的表进行备份,并生成sql文件输出到指定目录
具体代码逻辑
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
/**
* @author
* 执行mysql数据备份
*/
@Service
public class MysqlDumpExec {
private static final Logger logger = LoggerFactory.getLogger(MysqlDumpExec.class);
/**
* 备份文件输出路径
*/
@Value("${mysql.data.backup.filePath:}")
private String filePath;
/**
* mysqldump的可执行文件路径(一般在mysql安装目录的bin目录下)
*/
@Value("${mysql.data.backup.mysqlDumpPath:/usr/local/mysql/bin/}")
private String mysqlDumpPath;
/**
* 要备份的数据库表名 空格隔开
*/
@Value("${mysql.data.backup.tables:test_t1 test_t2}")
private String tables;
@Value("${spring.datasource.url:}")
private String dataUrl;
@Value("${spring.datasource.username:}")
private String dataUserName;
@Value("${spring.datasource.password:}")
private String dataPwd;
public String backUpSystemLogs() {
String backPath = "";
try {
String fileName = "backUpSystemLog_" + new SimpleDateFormat("yyyyMMdd_hhmmss").format(System.currentTimeMillis()) + "_sql.sql";
backPath = filePath + "/" + fileName;
//开始拼接命令
StringBuilder command = appendCmd(backPath);
// 调用外部执行exe文件的javaAPI
logger.info("执行mysql指令:" + command);
String os = System.getProperty("os.name");
Process process = null;
if(os.toLowerCase().startsWith("win")){
process = Runtime.getRuntime().exec(command.toString());
} else {
process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", command.toString()});
}
// 0 表示线程正常终止。
if (process.waitFor() != 0) {
logger.error("===========备份失败================");
} else {
logger.info("===========备份成功================");
process.destroy();
}
} catch (UnsupportedEncodingException e) {
logger.error("backUpSystemLogs UnsupportedEncodingException", e);
throw new ServiceException(SystemLogErrorCode.BACKUP_INVALID_PARAMETER);
} catch (IOException e) {
logger.error("backUpSystemLogs IOException", e);
throw new ServiceException(SystemLogErrorCode.BACKUP_INVALID_PARAMETER);
} catch (InterruptedException e) {
logger.error("backUpSystemLogs InterruptedException", e);
throw new ServiceException(SystemLogErrorCode.BACKUP_INVALID_PARAMETER);
}
if (logger.isDebugEnabled()) {
logger.debug("Execute MysqlDumpExec::backUpSystemLogs() end.");
}
return backPath;
}
/**
* 拼接执行命令
*
* @return
*/
private StringBuilder appendCmd(String backPath) {
StringBuilder command = new StringBuilder();
String mysqlIp = "";
String mysqlPort = "";
String mysqlData = "";
if (StringUtils.isNotBlank(dataUrl)) {
//从配置中解析出数据库ip+名+端口
String[] split = dataUrl.split("\\?")[0].split("/");
mysqlData = split[split.length - 1];
//192.144.210.30:33306
String allIp = split[split.length - 2];
String[] ips = allIp.split(":");
mysqlIp = ips[0];
mysqlPort = ips[1];
}
// 拼接命令行的命令
//windows: D:\MySQL Server 5.7\bin\mysqldump --opt -h127.0.0.1 --user=root -P33306 --password=123#! --databases test --tables test_user --result-file=D:\logsql\sql.sql --default-character-set=utf8
//linux: /usr/local/mysql/bin/mysqldump --opt -h111.144.210.30 --user=root --password='23!@#' -P33306 --databases test --tables test_t1 test_t2 --result-file=/syslog/backUpSystemLog_20211118_030951_sql.sql
//如果加了-t 是只导出数据不导出表结构
command.append(mysqlDumpPath+"mysqldump");
command.append(" --opt -h");
command.append(mysqlIp);
command.append(" --user=");
command.append(dataUserName);
// 区分系统
String os = System.getProperty("os.name");
if(os.toLowerCase().startsWith("win")){
command.append(" --password=");
command.append(dataPwd);
} else {
//linux需要引号密码,否则会执行失败
command.append(" --password=");
command.append("'"+dataPwd+"'");
}
//端口用大写的P指向
command.append(" -P");
// 导出范围
if (StringUtils.isEmpty(tables)) {
//全部表
command.append(" --all-databases");
} else {
//指定表
command.append(mysqlPort + " --databases " + mysqlData + " --tables ");
command.append(tables);
}
command.append(" --result-file=");
command.append(backPath);
return command;
}
}
遇到的问题
一、windows命令的密码用引号包含–password='123#@!'就执行失败,报错如下
mysqldump: Got error: 1045: Access denied for user ‘root’@‘124.65.8.8’ (using password: YES) when trying to connect
解决方案:把密码的双引号去掉
!!!(但是去掉双引号后在linux执行命令又会报错)
最终解决方案:
windows的时候–password不加引号
linux的时候–password加引号
二、linux的命令行单独在命令行执行没问题,但是放在代码里面就执行失败
类似这个命令
【/usr/local/mysql/bin/mysqldump --opt -h111.144.210.30 --user=root --password=‘23!@#’ -P33306 --databases test --tables test_t1 test_t2 --result-file=/syslog/backUpSystemLog_20211118_030951_sql.sql】
原因:可能是Runtime.getRuntime().exec(string);解析命令的时候,因为命令有特殊字符或者空格之类的,导致命令解析异常
解决方案:更改exec执行参数(针对linux !!!windows不能这样写)
process = Runtime.getRuntime().exec(new String[]{"/bin/sh", “-c”, command.toString()});
windows下:(本人没测试,不知道是否好用)
Runtime.getRuntime().exec(new String[]{ “cmd”, “/c”, cmds});
三、使用如下命令在linux不好用,但是windows好用
【/usr/local/mysql/bin/mysqldump -h111.144.210.30 -uroot -p’23!@#’ -P33306 test test_t1 > /log/test.sql】
原因: 目前本人没找到原因,请大佬指点
解决方案:直接换成以上代码中的命令