MYSQL本身提供了一个可执行程序 mysqldump
。它可以完成数据库的备份。
简单来说就是一个命令:
mysqldump -u[用户名] -p[密码] [数据库] > [备份的SQL文件]
# 注意 > 符号在linux下是重定向符,把标准输出重定向到文件
# 例如,备份demo库到c:/mysql.sql
mysqldump -uroot -proot demo > c:/mysql.sql
mysqldump的详细文档: https://dev.mysql.com/doc/refman/en/mysqldump.html
commons-exec
备份是通过新启动一个子进程完成。建议使用commons-exec
库,比较简单。它设计得比较合理,包含了子进程超时控制,异步执行等等功能。
Maven 坐标:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-exec -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
备份demo
package io.springboot.test;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;
import org.junit.Test;
import org.junit.runner.RunWith;
import io.springboot.DemoApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
/**
*
* 测试
* @author KevinBlandy
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@Slf4j
public class DataBaseBackUpTest {
@Autowired
private HikariDataSource hikariDataSource;
@Test
public void test () throws Exception {
// 备份的SQL文件
Path sqlFile = Paths.get("C:\\Users\\KevinBlandy\\Desktop\\db.sql");
String database = null;
// 执行SQL获取当前数据库的名称
try (Connection connection = hikariDataSource.getConnection()) {
try (ResultSet resultSet = connection.createStatement().executeQuery("SELECT DATABASE();")) {
if (resultSet.next()) {
database = resultSet.getString(1);
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
// 异常输出
ByteArrayOutputStream stdErr = new ByteArrayOutputStream();
// 标准输出。标准流输出的内容就是SQL的备份内容
OutputStream stdOut = new BufferedOutputStream(Files.newOutputStream(sqlFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING));
try (stdErr; stdOut) {
// 监视狗。执行超时时间,1小时
ExecuteWatchdog watchdog = new ExecuteWatchdog(TimeUnit.HOURS.toMillis(1));
// 子进程执行器
DefaultExecutor defaultExecutor = new DefaultExecutor();
// defaultExecutor.setWorkingDirectory(null); // 工作目录
defaultExecutor.setWatchdog(watchdog);
defaultExecutor.setStreamHandler(new PumpStreamHandler(stdOut, stdErr));
// 进程执行命令
CommandLine commandLine = new CommandLine("mysqldump");
commandLine.addArgument("-u" + hikariDataSource.getUsername()); // 用户名
commandLine.addArgument("-p" + hikariDataSource.getPassword()); // 密码
commandLine.addArgument(database); // 数据库
log.info("导出SQL数据...");
// 同步执行,阻塞直到子进程执行完毕。
int exitCode = defaultExecutor.execute(commandLine);
if(defaultExecutor.isFailure(exitCode) && watchdog.killedProcess()) {
log.error("备份超时");
}
log.info("SQL数据导出完毕: exitCode={}, sqlFile={}", exitCode, sqlFile.toString());
} catch (Exception e) {
log.error("SQL数据导出异常: {}", e.getMessage());
log.error("std err: {}{}", System.lineSeparator(), stdErr.toString());
}
}
}