定时导出指定数据库的指定表导出到指定数据库
一、Java实现
1、contronller
@Slf4j
@Controller
public class BackupController {
@Autowired
BackupService backupService;
// 备份
// @ResponseBody
// @PostMapping("/backup/backupByfile")
@Scheduled(cron="1 * * * * *")
public void backupByfile() {
backupService.backupByfile();
}
}
2、实现类
import lombok.extern.slf4j.Slf4j;
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.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import vip.xiaonuo.modular.ATest.Backup.service.BackupService;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.sql.*;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j
@Service
public class BackupServiceImpl implements BackupService {
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
// db 源数据库 backupDb 备份数据库
private String db = "qqq";
private String backupDb = "aa";
private String tableName = "user";
private Duration maxAge = Duration.ofMinutes(3);
private final Lock lock = new ReentrantLock();
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmmss.SSS");
/**
* 2、备份到指定文件夹
*/
// 备份目录
private String dir = "D:\\backups";
@Override
public void backupByfile() {
File directory = new File(dir);
if (directory.exists() && directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.delete()) {
log.info("File '{}' deleted successfully.", file.getName());
}
}
}
}
if (!this.lock.tryLock()) {
throw new RuntimeException("备份任务进行中!");
}
try {
String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
Path dir = Paths.get(this.dir);
// 备份的SQL文件
Path sqlFile = dir.resolve(Paths.get(now + ".sql"));
if (Files.exists(sqlFile)) {
// 文件已经存在,则添加后缀
for (int i = 1; i >= 1; i++) {
sqlFile = dir.resolve(Paths.get(now + "-" + i + ".sql"));
if (!Files.exists(sqlFile)) {
break;
}
}
}
// 初始化目录
if (!Files.isDirectory(sqlFile.getParent())) {
Files.createDirectories(sqlFile.getParent());
}
// 创建备份文件文件
Files.createFile(sqlFile);
// 标准流输出的内容就是 SQL 的备份内容
try (OutputStream stdOut = new BufferedOutputStream(
Files.newOutputStream(sqlFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
// 监视狗。执行超时时间,1小时
ExecuteWatchdog watchdog = new ExecuteWatchdog(TimeUnit.HOURS.toMillis(1));
// 子进程执行器
DefaultExecutor defaultExecutor = new DefaultExecutor();
// defaultExecutor.setWorkingDirectory(null); // 工作目录
defaultExecutor.setWatchdog(watchdog);
defaultExecutor.setStreamHandler(new PumpStreamHandler(stdOut, System.err));
// 进程执行命令
CommandLine commandLine = new CommandLine("mysqldump");
commandLine.addArgument("-u" + this.username); // User name
commandLine.addArgument("-p" + this.password); // Password
commandLine.addArgument(this.db); // Database name
commandLine.addArgument(this.tableName); // Table name
log.info("备份 SQL 数据");
// 同步执行,阻塞直到子进程执行完毕。
int exitCode = defaultExecutor.execute(commandLine);
if (defaultExecutor.isFailure(exitCode)) {
throw new RuntimeException("备份任务执行异常:exitCode=" + exitCode);
}
}
// 替换备份文件中的表名
replaceTableName(sqlFile, this.tableName, this.tableName + "_" + now);
// 导入 SQL 文件
String path = sqlFile.toAbsolutePath().toString();
importSql(path);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
this.lock.unlock();
}
}
private void replaceTableName(Path sqlFile, String oldTableName, String newTableName) throws IOException {
List<String> lines = Files.readAllLines(sqlFile, StandardCharsets.UTF_8);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.contains(oldTableName)) {
lines.set(i, line.replace(oldTableName, newTableName));
}
}
Files.write(sqlFile, lines, StandardCharsets.UTF_8);
}
public void importSql(String fPath) {
try {
Runtime rt = Runtime.getRuntime();
Process child = rt.exec("mysql -u" + this.username + " -p" + this.password + " " + this.backupDb);
OutputStream out = child.getOutputStream();
String inStr;
StringBuilder sb = new StringBuilder ("");
String outStr;
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fPath), "utf8"));
while ((inStr = br.readLine()) != null) {
sb.append(inStr + "\r\n");
}
outStr = sb.toString();
try (OutputStreamWriter writer = new OutputStreamWriter(out, "utf8")) {
writer.write(outStr);
writer.flush();
}
int exitCode = child.waitFor();
if (exitCode != 0) {
throw new RuntimeException("导入 SQL 文件失败,退出码: " + exitCode);
}
log.info("导入 SQL 文件成功!");
} catch (Exception e) {
throw new RuntimeException("出错了," + e);
}
}
}
3、执行结果二、sh脚本实现
1-1、新建一个.bash脚本(没有切换指定字段名)
#!/bin/bash
# 源数据库配置
SOURCE_DB="qhd"
SOURCE_HOST="localhost" # 或者改为实际的 IP 地址
SOURCE_PORT="13306"
SOURCE_USER="root"
SOURCE_PASS="123456"
# 目标数据库配置
TARGET_DB="aa"
TARGET_HOST="localhost" # 或者改为实际的 IP 地址
TARGET_PORT="13306"
TARGET_USER="root"
TARGET_PASS="123456"
# 要导出的表
SOURCE_TABLE="owner"
CURRENT_DATETIME=$(date +"%Y%m%d%H%M%S")
EXPORT_TABLE_NAME="${SOURCE_TABLE}_${CURRENT_DATETIME}"
# 导出 SQL 文件的路径
EXPORT_DIR="/home/mengqingda/program/test/file"
EXPORT_FILE="$EXPORT_DIR/${EXPORT_TABLE_NAME}.sql"
# 创建导出目录(如果不存在)
mkdir -p "$EXPORT_DIR"
echo "开始导出 $EXPORT_TABLE_NAME 表数据..."
# 导出指定表的数据
mysqldump -h$SOURCE_HOST -P$SOURCE_PORT -u$SOURCE_USER -p$SOURCE_PASS $SOURCE_DB $SOURCE_TABLE > $EXPORT_FILE
if [ $? -eq 0 ]; then
echo "数据导出完成, 开始导入 $EXPORT_TABLE_NAME 表数据到 $TARGET_DB 数据库..."
# 导入数据到目标数据库
mysql -h$TARGET_HOST -P$TARGET_PORT -u$TARGET_USER -p$TARGET_PASS $TARGET_DB -e "CREATE TABLE $EXPORT_TABLE_NAME LIKE $SOURCE_DB.$SOURCE_TABLE; INSERT INTO $EXPORT_TABLE_NAME SELECT * FROM $SOURCE_DB.$SOURCE_TABLE;"
if [ $? -eq 0 ]; then
echo "数据导入完成!"
else
echo "数据导入失败!"
fi
else
echo "数据导出失败!"
fi
1-2、新建一个.bash脚本(切换指定字段名)
#!/bin/bash
# 源数据库配置
SOURCE_DB="qhd"
SOURCE_HOST="localhost" # 或者改为实际的 IP 地址
SOURCE_PORT="3306"
SOURCE_USER="root"
SOURCE_PASS="123456"
# 目标数据库配置
TARGET_DB="aa"
TARGET_HOST="localhost" # 或者改为实际的 IP 地址
TARGET_PORT="3306"
TARGET_USER="root"
TARGET_PASS="123456"
# 要导出的表
SOURCE_TABLE="user"
CURRENT_DATETIME=$(date +"%Y%m%d%H%M%S")
EXPORT_TABLE_NAME="${SOURCE_TABLE}_${CURRENT_DATETIME}"
# 导出 SQL 文件的路径
EXPORT_DIR="D:\ProgramFiles\programSoftware\backups"
EXPORT_FILE="$EXPORT_DIR/${EXPORT_TABLE_NAME}.sql"
# 创建导出目录(如果不存在)
mkdir -p "$EXPORT_DIR"
echo "开始导出 $EXPORT_TABLE_NAME 表数据..."
# 导出指定表的数据
mysqldump -h$SOURCE_HOST -P$SOURCE_PORT -u$SOURCE_USER -p$SOURCE_PASS $SOURCE_DB $SOURCE_TABLE >$EXPORT_FILE
if [ $? -eq 0 ]; then
echo "数据导出完成, 开始修改 SQL 文件..."
echo "SQL 文件修改完成, 开始创建目标表..."
# 创建目标表
mysql -h$TARGET_HOST -P$TARGET_PORT -u$TARGET_USER -p$TARGET_PASS $TARGET_DB -e "
CREATE TABLE IF NOT EXISTS $EXPORT_TABLE_NAME LIKE $SOURCE_DB.$SOURCE_TABLE;
ALTER TABLE $EXPORT_TABLE_NAME
CHANGE xm 姓名 VARCHAR(255),
CHANGE bj 班级 VARCHAR(255),
CHANGE xh 学号 VARCHAR(255),
CHANGE wx 微信 VARCHAR(255),
CHANGE qq qq号 VARCHAR(255),
CHANGE zw 职位 VARCHAR(255),
CHANGE xl 学历 VARCHAR(255);
"
echo "目标表创建完成, 开始导入数据..."
# 导入数据到目标表
mysql -h$TARGET_HOST -P$TARGET_PORT -u$TARGET_USER -p$TARGET_PASS $TARGET_DB -e "
INSERT INTO $EXPORT_TABLE_NAME SELECT * FROM $SOURCE_DB.$SOURCE_TABLE;
"
if [ $? -eq 0 ]; then
echo "数据导入完成!"
else
echo "数据导入失败!"
fi
else
echo "数据导出失败!"
fi
2、在linux打开定时任务执行脚本(因为定时只有linux 可以执行)
(1)、先赋予文件夹权限
chmod -R 777 /home/mengqingda/program/test
(2)、然后执行
crontab -e
(3)、如果不习惯用这个编译器可以切换vim编译器进行编辑
export EDITOR="/usr/bin/vim" ;
(4)、添加定时任务(5分钟一次)
*/5 * * * * /home/mengqingda/program/test/backup.sh