main
黄海 6 months ago
parent ed7b56ccfc
commit 8df0f1baca

@ -0,0 +1,183 @@
package com.dsideal.YunXiaoTools.Service;
import java.io.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class DatabaseRestorer {
private final String jdbcUrl;
private final String username;
private final String password;
public DatabaseRestorer(String jdbcUrl, String username, String password) {
this.jdbcUrl = jdbcUrl;
this.username = username;
this.password = password;
}
/**
*
*/
public void restore(String sqlFile) {
try {
// 读取SQL文件内容
List<String> sqlStatements = parseSqlFile(sqlFile);
System.out.println("SQL文件解析完成共 " + sqlStatements.size() + " 条语句");
// 执行SQL语句
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
conn.setAutoCommit(false); // 开启事务
try (Statement stmt = conn.createStatement()) {
int total = sqlStatements.size();
int current = 0;
for (String sql : sqlStatements) {
if (sql.trim().length() > 0) {
try {
stmt.execute(sql);
current++;
// 显示进度
if (current % 100 == 0 || current == total) {
System.out.printf("\r还原进度: %.2f%% (%d/%d)",
(current * 100.0 / total), current, total);
}
} catch (SQLException e) {
System.err.println("\n执行SQL出错: " + sql);
System.err.println("错误信息: " + e.getMessage());
throw e;
}
}
}
conn.commit(); // 提交事务
System.out.println("\n数据库还原完成");
} catch (SQLException e) {
conn.rollback(); // 发生错误时回滚
throw e;
}
}
} catch (Exception e) {
throw new RuntimeException("还原数据库失败: " + e.getMessage(), e);
}
}
/**
* SQL
*/
private List<String> parseSqlFile(String sqlFile) throws IOException {
List<String> sqlStatements = new ArrayList<>();
StringBuilder statement = new StringBuilder();
boolean inString = false;
try (BufferedReader reader = new BufferedReader(new FileReader(sqlFile))) {
String line;
while ((line = reader.readLine()) != null) {
// 跳过注释行
if (line.trim().startsWith("--") || line.trim().startsWith("/*")) {
continue;
}
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
// 处理字符串
if (c == '\'') {
inString = !inString;
}
statement.append(c);
// 如果遇到分号且不在字符串中,说明一条语句结束
if (c == ';' && !inString) {
sqlStatements.add(statement.toString());
statement = new StringBuilder();
}
}
statement.append("\n");
}
// 处理最后一条可能没有分号的语句
if (statement.length() > 0) {
sqlStatements.add(statement.toString());
}
}
return sqlStatements;
}
/**
*
*/
public void restore(String sqlFile, ProgressCallback callback) {
try {
callback.onStart("正在解析SQL文件...");
List<String> sqlStatements = parseSqlFile(sqlFile);
callback.onProgress("开始执行SQL语句", 0);
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
conn.setAutoCommit(false);
try (Statement stmt = conn.createStatement()) {
int total = sqlStatements.size();
int current = 0;
long startTime = System.currentTimeMillis();
for (String sql : sqlStatements) {
if (sql.trim().length() > 0) {
try {
stmt.execute(sql);
current++;
// 计算进度和速度
if (current % 10 == 0 || current == total) {
int progress = (int) ((current * 100.0) / total);
long elapsed = System.currentTimeMillis() - startTime;
double speed = current * 1000.0 / elapsed;
callback.onProgress(
String.format("已执行 %d/%d 条语句 (%.2f条/秒)",
current, total, speed),
progress
);
}
} catch (SQLException e) {
callback.onError("执行SQL出错: " + sql, e);
throw e;
}
}
}
conn.commit();
callback.onComplete("数据库还原完成");
} catch (SQLException e) {
conn.rollback();
throw e;
}
}
} catch (Exception e) {
callback.onError("还原失败", e);
throw new RuntimeException("还原数据库失败: " + e.getMessage(), e);
}
}
/**
*
*/
public interface ProgressCallback {
void onStart(String message);
void onProgress(String message, int percent);
void onError(String message, Exception e);
void onComplete(String message);
}
}

@ -40,22 +40,26 @@ public class MysqlBackupService {
// 获取mysqldump路径
String mysqldumpPath = CommonUtil.getMySQLDumpPath();
// 构建命令(不包含密码)
// MariaDB 兼容的备份命令
ProcessBuilder pb = new ProcessBuilder(
mysqldumpPath,
"-h" + host,
"-P" + port,
"-u" + user,
//"--ssl-mode=DISABLED",
"--skip-ssl", // MariaDB 使用 --skip-ssl
"--no-tablespaces", // 避免权限问题
"--skip-ssl",
"--no-tablespaces",
"--default-character-set=utf8",
"--single-transaction",
"--routines",
"--triggers",
"--events",
"--complete-insert",
"--add-drop-database",
"--add-drop-table",
"--single-transaction", // 保证数据一致性
"--routines", // 包含存储过程
"--triggers", // 包含触发器
"--events", // 包含事件
"--complete-insert", // 完整insert语句
"--add-drop-database", // 添加drop database语句
"--add-drop-table", // 添加drop table语句
"--skip-lock-tables", // 跳过锁表
"--compact", // 产生更紧凑的输出
"--skip-extended-insert", // 分行显示insert语句
database,
"-r",
backupPath

@ -3,33 +3,12 @@ package com.dsideal.YunXiaoTools.Service;
import com.dsideal.YunXiaoTools.Utils.CommonUtil;
import com.dsideal.YunXiaoTools.Utils.MultiThreadDownloader;
import com.jfinal.kit.PropKit;
import com.obs.services.ObsClient;
import java.io.*;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.zip.*;
public class MysqlRestoreService {
// 目标数据库配置
private final String dbHost;
private final String dbPort;
private final String dbUser;
private final String dbPassword;
private final String dbName;
public MysqlRestoreService() {
// 目标数据库配置
String jdbcUrl = PropKit.get("write.jdbcUrl");
this.dbHost = CommonUtil.getHostFromJdbcUrl(jdbcUrl);
this.dbPort = CommonUtil.getPortFromJdbcUrl(jdbcUrl);
this.dbUser = PropKit.get("write.user");
this.dbPassword = PropKit.get("write.password");
this.dbName = CommonUtil.getDatabaseFromJdbcUrl(jdbcUrl);
}
/**
*
@ -51,7 +30,8 @@ public class MysqlRestoreService {
System.out.println("文件解压完成: " + sqlFile);
// 3. 还原数据库
restoreDatabase(sqlFile);
DatabaseRestorer dr=new DatabaseRestorer(PropKit.get("write.jdbcUrl"), PropKit.get("write.user"), PropKit.get("write.password"));
dr.restore(sqlFile);
System.out.println("数据库还原完成");
} catch (Exception e) {
@ -119,84 +99,6 @@ public class MysqlRestoreService {
return extractedFile;
}
private void restoreDatabase(String sqlFile) {
try {
String mysqlPath = CommonUtil.getMySQLPath();
ProcessBuilder pb = new ProcessBuilder(
mysqlPath,
"-h" + dbHost,
"-P" + dbPort,
"-u" + dbUser,
"--skip-ssl",
dbName
);
pb.environment().put("MYSQL_PWD", dbPassword);
// 启动进程
Process process = pb.start();
// 使用多线程处理输入输出流,避免死锁
ExecutorService executor = Executors.newFixedThreadPool(2);
// 处理错误输出流
Future<?> errorFuture = executor.submit(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.contains("WARNING")) {
System.out.println("还原进度: " + line);
}
}
} catch (IOException e) {
e.printStackTrace();
}
});
// 处理输入流SQL文件
Future<?> inputFuture = executor.submit(() -> {
try (BufferedInputStream sqlInput = new BufferedInputStream(new FileInputStream(sqlFile));
BufferedOutputStream processOutput = new BufferedOutputStream(process.getOutputStream())) {
byte[] buffer = new byte[8192];
int len;
long total = 0;
long fileSize = new File(sqlFile).length();
while ((len = sqlInput.read(buffer)) != -1) {
processOutput.write(buffer, 0, len);
total += len;
// 显示进度
int progress = (int) ((total * 100) / fileSize);
System.out.printf("\r导入进度: %d%%", progress);
}
// 重要:刷新并关闭输出流
processOutput.flush();
processOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
});
// 等待输入输出处理完成
try {
inputFuture.get();
errorFuture.get();
} catch (Exception e) {
throw new RuntimeException("处理输入输出流失败", e);
} finally {
executor.shutdown();
}
// 等待进程完成
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("数据库还原失败,退出码: " + exitCode);
}
System.out.println("\n数据库还原完成");
} catch (Exception e) {
throw new RuntimeException("还原数据库失败: " + e.getMessage(), e);
}
}
public static void main(String[] args) {
PropKit.use("application.properties");

@ -12,9 +12,9 @@ write.password=Password123@mysql
write.jdbcUrl=jdbc:mysql://10.10.14.203:3306/HuangHai_Test?rewriteBatchedStatements=true&useUnicode=true&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false
# mysqldump执行文件位置
mysqldump_path=D:/ffmpeg/mysqldump.exe
mysqldump_path=C:/Program Files/MariaDB 11.6/bin/mysqldump.exe
# mysql执行文件位置
mysql_path=D:/ffmpeg/mysql.exe
mysql_path=C:/Program Files/MariaDB 11.6/bin/mysql.exe
# 当前是读取方还是写入方 1:读取 2写入
DEPLOY_ID=1

@ -12,9 +12,9 @@ write.password=Password123@mysql
write.jdbcUrl=jdbc:mysql://10.10.14.203:3306/HuangHai_Test?rewriteBatchedStatements=true&useUnicode=true&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false
# mysqldump执行文件位置
mysqldump_path=D:/ffmpeg/mysqldump.exe
mysqldump_path=C:/Program Files/MariaDB 11.6/bin/mysqldump.exe
# mysql执行文件位置
mysql_path=D:/ffmpeg/mysql.exe
mysql_path=C:/Program Files/MariaDB 11.6/bin/mysql.exe
# 当前是读取方还是写入方 1:读取 2写入
DEPLOY_ID=1

Loading…
Cancel
Save