本文介绍了Mysql数据库和达梦数据库全量、增量、自定义备份与恢复。以及文件资源以压缩包的形式实现备份与恢复。
使用java定时任务实现备份与恢复操作。
windows:
在mysql安装目录下的my.ini文件中添加以下配置:
# dir\[filename] dir为日志存放目录 mysql-bin为新建文件夹 filename为日志文件名 形式为filename.number number的形式为000001、000002等 会自动递增
log-bin=D:\\mysql-8.0.28-winx64\\Data\\mysql-bin\\mylog
binlog_format=Mixed
#需要备份的数据库名,如果备份多个数据库,重复设置这个选项即可
binlog-do-db=testbackup
#不需要备份的数据库名,如果备份多个数据库,重复设置这个选项即可
#binlog-ignore-db=
skip_ssl
liunx:
在my.cnf中添加以下配置:
#开启log——bin日志
log-bin=/usr/local/mysql/mylog
binlog_format=Mixed
#需要备份的数据库名,如果备份多个数据库,重复设置这个选项即可
binlog-do-db=testbackup
#不需要备份的数据库名,如果备份多个数据库,重复设置这个选项即可
#binlog-ignore-db=
skip_ssl
然后重启数据库使配置生效
mysqldump -hIP -u用户名 -p密码 -P端口号 数据库名 --ssl-mode=DISABLED --result-file=/disk/backup/full/add/def_linux/linux_mysql8/MYSQL_20230228112504.sql(备份文件地址及文件名) --default-character-set=utf8
./mysql -hIP -u用户名 -p密码 -P端口号 -A -D 数据库名 -e "SOURCE /disk/backup/full/add/def_linux/linux_mysql8/MYSQL_20230228112504.sql"
首先全量备份一次,然后利用binlog日志,定时刷新日志生成新的日志文件,备份与恢复均依赖于binlog日志实现
mysqldump -hIP -u用户名 -p密码 -P端口号 数据库名 --ssl-mode=DISABLED --result-file=/disk/backup/full/add/def_linux/linux_mysql8/MYSQL_ADDBACKUP_FULL.sql --default-character-set=utf8
每次增量备份即为刷新日志生成新的日志文件
./mysqladmin -hIP -u用户名 -p密码 -P端口号 flush-logs
首先全量恢复一次
./mysql -hIP -u用户名 -p密码 -P端口号 -A -D 数据库名 -e "SOURCE /disk/backup/full/add/def_linux/linux_mysql8/MYSQL_ADDBACKUP_FULL.sql"
然后按日志生成顺序挨个恢复,可指定要恢复到的时间
./mysqlbinlog --no-defaults /usr/local/mysql/mylog.000004 |mysql -hIP -u用户名 -p密码 -P端口号
命令行连接远程达梦数据库并进入dlsql命令行:
disql 模式名/密码@IP:端口号
达梦数据库备份与恢复有多种方式,本文采用逻辑备份,无需停掉达梦数据库服务。
逻辑备份:
导出:dexp / 导入:dimp
逻辑导出和逻辑导入数据库对象分为四种级别:数据库级、用户级、模式级和表级。四种级别独立互斥,不能同时存在。四种级别所提供的功能:
数据库级(FULL):导出或导入整个数据库中的全部对象。
用户级(OWNER):导出或导入一个或多个用户所拥有的全部对象。模式级(SCHEMAS):导出或导入一个或多个模式下的全部对象。
表级(TABLE):导出或导入一个或多个指定的表或表分区。
首先介绍几个配置命令:
TABLE_EXISTS_ACTION=[SKIP | APPEND | TRUNCATE | REPLACE] 用于要导入的表已经存在时的处理方式。默认为直接报错
SKIP:跳过此表
APPEND:直接向现有表中导入数据。
TRUNCATE:先删除现有表中的数据,再向表中导入数据。
REPLACE:先删除现有表,再导数据。
TABLES选项支持模糊匹配
FUZZY_MATCH:用于指定 TABLES 选项是否支持模糊匹配,取值Y/N
FUZZY_MATCH=N:表示TABLES 选项不支持模糊匹配,指定的表名与数据库中的表名必须精确匹配;
FUZZY_MATCH=Y:TABLES选项支持模糊匹配,指定的表名与数据库中的表名采用LIKE模糊查询匹配;
示例
2.1.1、导出XMLTEST模式中,表名包含YG的所有表
[dmdba@localhost bin]$ ./dexp SYSDBA/SYSDBA@LOCALHOST:5237 DIRECTORY=/home/dmdba file=dexp_tab_fz.dmp log=dexp_tab_fz.log tables=XMLTEST.%YG% fuzzy_match=Y
2.1.2、导出XMLTEST模式中,表名以T字母开头的所有表
[dmdba@localhost bin]$ ./dexp SYSDBA/SYSDBA@LOCALHOST:5237 DIRECTORY=/home/dmdba file=dexp_tab_fz1.dmp log=dexp_tab_fz1.log tables=XMLTEST.T% fuzzy_match=Y
2.1.3、导出XMLTEST模式中,表名最后两个字母为BU的所有表
[dmdba@localhost bin]$ ./dexp SYSDBA/SYSDBA@LOCALHOST:5237 DIRECTORY=/home/dmdba file=dexp_tab_fz2.dmp log=dexp_tab_fz2.log tables=XMLTEST.%BU fuzzy_match=Y
2.2 导出表数据时,只导出符合条件的数据
QUERY选项:用于指定过滤条件来对表数据进行导出。
QUERY="" ,QUERY选项的值为where条件(SQL语句中的where条件),比如QUERY="WHERE DEPT=20"
示例:
[dmdba@localhost bin]$ ./dexp SYSDBA/SYSDBA@LOCALHOST:5236 DIRECTORY=/home/dmdba file=dexp_tab_q1.dmp log=dexp_tab_q1.log tables=XMLTEST.T_EMP,XMLTEST.T1 query="where deptno=10"
当指定多个表时,会对每个表都进行条件过滤,如果指定的表不存在条件列,则导出时会出现警告信息,此时该表导出失败,但不影响其他表的导出。
表存在时的操作:往表中插入数据:TABLE_EXISTS_ACTION=APPEND
主键冲突时的操作:PRIMARY_CONFLICT=IGNORE
备份与恢复命令均要到达梦安装目录bin目录下执行
全表备份导出
dexp 模式名/密码@IP:端口号 file=dexp_tab02.dmp log=dexp_tab02.log
全表导入 TABLE_EXISTS_ACTION:TABLE_EXISTS_ACTION 用于要导入的表已经存在时的处理方式。默认为直接报错
dimp 模式名/密码@IP:端口号 file=dexp_tab02.dmp log=dexp_tab03.log TABLE_EXISTS_ACTION=TRUNCATE
自定义表导入导出
自定义表备份导出 file= 将备份文件输出到指定文件,可以加绝对路径(建议不加,直接将文件生成在bin目录下) log= 日志文件名 tables= 要备份的表名,多个表名用,隔开 不加tables=即备份全部的表
dexp 模式名/密码@IP:端口号 file=dexp_tab01.dmp log=dexp_tab01.log tables=user,dept
自定义表导入恢复 file=上次备份导出的文件 log= 新的日志文件名 tables= 要恢复的表名,多个表名用,隔开 不加tables=即恢复全部的表 TABLE_EXISTS_ACTION PRIMARY_CONFLICT:主键冲突时的处理方式: IGNORE|OVERWRITE 默认报错
dimp 模式名/密码@IP:端口号 file=dexp_tab111.dmp log=dexp_tab112.log tables=user,dept TABLE_EXISTS_ACTION=APPEND PRIMARY_CONFLICT=IGNORE
dimp 模式名/密码@IP:端口号 file=dexp_tab01.dmp log=dexp_tab01.log tables=user,dept TABLE_EXISTS_ACTION=TRUNCATE
增量备份导出 加where条件 where created >=‘2023-02-25 00:00:00’; 有where条件时必须加tables=
dexp 模式名/密码@IP:端口号 file=dexp_tab08.dmp log=dexp_tab08.log tables=user,dept QUERY="where created >= to_date('2022-02-20 00:00:00','yyyy-mm-dd hh24:mi:ss')";
增量备份导入
先执行全量恢复 然后按顺序恢复(直接向现有表中增加数据)
dimp 模式名/密码@IP:端口号 file=dexp_tab08.dmp log=dexp_tab09.log tables=user,dept TABLE_EXISTS_ACTION=APPEND
全量备份:
./dexp 模式名/密码@IP:端口号file=/disk/backup/full/add/def_linux/linux_dm8/MYSQL_20230228151106.dmp log=/disk/backup/full/add/def_linux/linux_dm8/MYSQL_20230228151106.log
全量恢复:
./dimp 模式名/密码@IP:端口号file=/disk/backup/full/add/def_linux/linux_dm8/MYSQL_20230228151106.dmp log=restore_20230228151951.log TABLE_EXISTS_ACTION=TRUNCATE
增量备份:
先全备一次 tables= 可指定备份表
./dexp 模式名/密码@IP:端口号file=/disk/backup/full/add/def_linux/linux_dm8/20230228153236/DAMENG_ADDBACKUP_FULL.dmp log=/disk/backup/full/add/def_linux/linux_dm8/20230228153236/DAMENG_ADDBACKUP_FULL.log tables=user,dept
再加条件备份
./dexp 模式名/密码@IP:端口号file=/disk/backup/full/add/def_linux/linux_dm8/20230228153236/MYSQL_20230228154011.dmp log=/disk/backup/full/add/def_linux/linux_dm8/20230228153236/MYSQL_20230228154011.log tables=user,dept QUERY="where created >= to_date('2023-02-28 15:02:36','yyyy-mm-dd hh24:mi:ss')"
增量恢复:
先恢复全量的:
./dimp 模式名/密码@IP:端口号file=/disk/backup/full/add/def_linux/linux_dm8/20230228153236/DAMENG_ADDBACKUP_FULL.dmp log=restore_20230228154502.log tables=user,dept TABLE_EXISTS_ACTION=TRUNCATE
然后按顺序恢复 表存在时的操作:往表中插入数据:TABLE_EXISTS_ACTION=APPEND 主键冲突时的操作:PRIMARY_CONFLICT=IGNORE
./dimp 模式名/密码@IP:端口号 file=/disk/backup/full/add/def_linux/linux_dm8/20230228153236/MYSQL_20230228154255.dmp log=restore_20230228154502.log tables=user,dept TABLE_EXISTS_ACTION=APPEND PRIMARY_CONFLICT=IGNORE
文件备份:
linux:
tar -czvf /disk/backup/full/add/def_linux/files/uploadFiles-backup-20230228112504.tar.gz /disk/files/
解压
tar -zxvf /disk/backup/full/add/def_linux/files/uploadFiles-backup-20230228112504.tar.gz -C /
windows:
tar -czvf C:\disk\backup\full\add\def_linux\files\uploadFiles-backup-20230228112504.tar.gz C:\disk\files\
解压
tar -zxvf C:\disk/backup\full\add\def_linux\files\uploadFiles-backup-20230228112504.tar.gz -C
@ApiOperation("执行数据备份")
@PostMapping(Routes.BackupRoutes.INIT)
@PreAuthorize("@ss.hasPermi('data:backup:start')")
@Log(title = "数据备份", businessType = BusinessType.START)
public JsonData initBackup(@RequestBody BackupInsertParams operLogParam) {
try {
boolean flag = false;
if(dataSourceFlag.equals("MYSQL")){
flag = mySqlBackupService.initBackup(operLogParam);
}else {
flag = damengBackupService.initBackup(operLogParam);
}
// 返回结果
if(flag){
backupMapper.updateStatusById(operLogParam.getId(),2);
return JsonData.buildSuccess("执行成功");
}else{
return JsonData.buildSuccess("执行失败");
}
} catch (Exception e) {
e.printStackTrace();
return JsonData.buildSuccess("执行失败");
}
}
@ApiOperation("执行数据恢复")
@GetMapping(Routes.BackupRoutes.RESTORE)
@PreAuthorize("@ss.hasPermi('data:restore:start')")
@Log(title = "数据恢复", businessType = BusinessType.START)
public JsonData restoreBackUp(@RequestParam String id,@RequestParam(required = false) String fileName) {
try {
boolean flag = false;
if(dataSourceFlag.equals("MYSQL")){
flag = mySqlBackupService.restoreBackUp(id,fileName);
}else {
flag = damengBackupService.restoreBackUp(id,fileName);
}
// 返回结果
if(flag){
return JsonData.buildSuccess("执行成功");
}else{
return JsonData.buildSuccess("执行失败");
}
} catch (Exception e) {
e.printStackTrace();
return JsonData.buildSuccess("执行失败");
}
}
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BackupParams {
@ApiModelProperty("主机IP")
private String host;
@ApiModelProperty("数据库用户名")
private String userName;
@ApiModelProperty("数据库密码")
private String password;
@ApiModelProperty("数据库端口号")
private String port;
@ApiModelProperty("备份的路径/上传文件的路径")
private String backupFolderPath;
@ApiModelProperty("备份的文件名")
private String fileName;
@ApiModelProperty("需要备份的数据库名称")
private String database;
@ApiModelProperty("mysql bin 目录所在位置")
private String mysqlPath;
@ApiModelProperty("备份类型 1:全量备份 2:增量备份 3:自定义全量备份")
private int backupType;
@ApiModelProperty("全量备份生成的备份文件的目录/上传文件压缩备份的路径")
private String backupFolder;
@ApiModelProperty("全量备份生成的备份文件的名称")
private String backupFileName;
@ApiModelProperty("操作是否成功")
private boolean resultStatus;
@ApiModelProperty("自定义全量备份的表名")
private String[] tables;
@ApiModelProperty("原始备份文件的名称(用于下次备份时删除该文件)")
private String originalBackupFileName;
@ApiModelProperty("是否为首次备份")
private boolean firstBackUpFlag = false;
@ApiModelProperty("上次备份时间")
private String lastTime ;
@ApiModelProperty("所有的表名")
private String allTables ;
}
mySqlBackupService.initBackup
@Override
public boolean initBackup(BackupInsertParams insertParams) throws Exception {
//ip
String host = properties.getHost();
//数据库用户名
String userName = properties.getUserName();
//数据库密码
String password = properties.getPassword();
//数据库名称
String database = properties.getDatabase();
String port=properties.getPort();
String mysqlPath = winMysqlPath;
String backupFolder = windowsBackupFolder;
if(systemLinuxFlag){
mysqlPath = linuxMysqlPath;
backupFolder = linuxBackupFolder;
}
//文件名
String fileName = dataSourceFlag +"_" + DateUtils.format(new Date(),BackupConstants.DATE_FORMAT)+BackupConstants.BACKUP_FILE_NAME;
//备份数据地址
String backupFolderPath = "";
if (StringUtils.isNotBlank(insertParams.getSavePath())){
backupFolderPath=insertParams.getSavePath();
}else {
backupFolderPath = backupFolder ;
}
/* 查询备份方式 */
DictVo dict = dictService.selectById(insertParams.getBackupTypeId());
insertParams.setBackupType(1);
if(dict != null){
if(dict.getDictItem().contains("全量")){
insertParams.setBackupType(1);
}else if(dict.getDictItem().contains("增量")){
insertParams.setBackupType(2);
}else if(dict.getDictItem().contains("自定义")){
insertParams.setBackupType(2);
}
}
BackupParams backupParams = new BackupParams(host, userName, password, port, backupFolderPath, fileName, database,mysqlPath,insertParams.getBackupType(),backupFolder,null,false,insertParams.getTables()!=null?insertParams.getTables().split(","):null,insertParams.getFileName(), insertParams.isFirstBackUpFlag(), insertParams.getStartTime(),null);
// 备份数据库
backupParams = mySQLBackupService.backup(backupParams);
// 备份数据库生成的文件目录
String dataBackupFolder = backupParams.getBackupFolderPath();
//将文件名更新到数据库中
BackupDO backupDO=new BackupDO();
// 全量备份可能生成新的文件名
String newFileName = backupParams.getFileName();
// 备份上传文件
backupParams.setOriginalBackupFileName(insertParams.getUploadFileSaveName());
BackupParams backupParams1 = filesBackUpService.backup(backupParams);
// 备份上传文件
// 备份数据库
if (backupParams.isResultStatus()){
BeanUtils.copyProperties(insertParams,backupDO);
backupDO.setSavePath(dataBackupFolder);
backupDO.setFileName(newFileName);
// 更新上次执行时间
backupDO.setLastTime(DateUtils.format(new Date(),BackupConstants.DATE_FORMAT1));
if (backupParams1.isResultStatus()){
backupDO.setUploadFileSavePath(backupParams1.getBackupFolder());
backupDO.setBackupStatus(1);
backupDO.setUploadFileSaveName(backupParams1.getFileName());
}
backupMapper.updateById(backupDO);
}
return backupParams.isResultStatus();
}
damengBackupService.initBackup
@Override
public boolean initBackup(BackupInsertParams insertParams) throws Exception {
//ip
String host = properties.getHost();
//数据库用户名
String userName = properties.getUserName();
//数据库密码
String password = properties.getPassword();
//数据库名称
String database = properties.getDatabase();
String port=properties.getPort();
String mysqlPath = winMysqlPath;
String backupFolder = windowsBackupFolder;
if(systemLinuxFlag){
mysqlPath = linuxMysqlPath;
backupFolder = linuxBackupFolder;
}
//文件名
String fileName = dataSourceFlag +"_" + DateUtils.format(new Date(),BackupConstants.DATE_FORMAT)+BackupConstants.DAMENG_EXT;
//备份数据地址
String backupFolderPath = "";
if (StringUtils.isNotBlank(insertParams.getSavePath())){
backupFolderPath=insertParams.getSavePath();
}else {
backupFolderPath = backupFolder ;
}
/* 查询备份方式 */
DictVo dict = dictService.selectById(insertParams.getBackupTypeId());
insertParams.setBackupType(1);
if(dict != null){
if(dict.getDictItem().contains("全量")){
insertParams.setBackupType(1);
}else if(dict.getDictItem().contains("增量")){
insertParams.setBackupType(2);
}else if(dict.getDictItem().contains("自定义")){
insertParams.setBackupType(2);
}
}
// 备份路径为空则代表为首次备份
if(StringUtils.isBlank(insertParams.getSavePath())){
insertParams.setFirstBackUpFlag(true);
}else{
insertParams.setFirstBackUpFlag(false);
}
// 增量备份指定文件夹 固定首次备份文件的名称
if(insertParams.getBackupType() == 2){
if(systemLinuxFlag){
backupFolderPath = backupFolderPath ;
if(insertParams.isFirstBackUpFlag()){
backupFolderPath = backupFolderPath + "/"+DateUtils.format(new Date(),BackupConstants.DATE_FORMAT);
}
}else{
backupFolderPath = backupFolderPath ;
if(insertParams.isFirstBackUpFlag()){
backupFolderPath = backupFolderPath + "\\"+DateUtils.format(new Date(),BackupConstants.DATE_FORMAT);
}
}
}
BackupParams backupParams = new BackupParams(host, userName, password, port, backupFolderPath, fileName, database,mysqlPath,insertParams.getBackupType(),
backupFolder,null,false,insertParams.getTables()!=null?insertParams.getTables().split(","):null,insertParams.getFileName(),insertParams.isFirstBackUpFlag(),insertParams.getStartTime(), daMengBackupService.selectAllTables());
// 备份数据库
backupParams = daMengBackupService.backup(backupParams);
// 备份数据库生成的文件目录
String dataBackupFolder = backupParams.getBackupFolderPath();
// 全量备份可能生成新的文件名
String newFileName = backupParams.getFileName();
// 备份上传文件
backupParams.setOriginalBackupFileName(insertParams.getUploadFileSaveName());
BackupParams backupParams1 = filesBackUpService.backup(backupParams);
// 备份上传文件
// 备份数据库
if (backupParams.isResultStatus()){
//将文件名更新到数据库中
BackupDO backupDO=new BackupDO();
BeanUtils.copyProperties(insertParams,backupDO);
// 更新上次执行时间
backupDO.setLastTime(DateUtils.format(new Date(),BackupConstants.DATE_FORMAT1));
backupDO.setSavePath(dataBackupFolder);
backupDO.setFileName(newFileName);
if (backupParams1.isResultStatus()){
backupDO.setUploadFileSavePath(backupParams1.getBackupFolder());
// 将状态改为执行中
backupDO.setBackupStatus(1);
backupDO.setUploadFileSaveName(backupParams1.getFileName());
}
backupMapper.updateById(backupDO);
}
return backupParams.isResultStatus();
}
mySQLBackupService.backup
@Override
public BackupParams backup(BackupParams backupParams) throws Exception {
if(backupParams.getBackupType() != 2){
// 全量备份
MySQLBackupRestoreUtils.backup(backupParams);
}else {
// 增量备份
MySQLBackupRestoreUtils.addBackup(backupParams);
}
return backupParams;
}
daMengBackupService.backup
@Override
public BackupParams backup(BackupParams backupParams) throws Exception {
if(backupParams.getBackupType() != 2){
// 全量备份
DamengBackupRestoreUtils.backup(backupParams);
}else {
// 增量备份
DamengBackupRestoreUtils.addBackup(backupParams);
}
return backupParams;
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import java.io.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* Mysql备份与恢复工具类
*/
@Slf4j
public class MySQLBackupRestoreUtils {
@Value("${backup.path.linux}")
private static String linuxBackupFolder;
@Value("${backup.path.windows}")
private static String windowsBackupFolder;
/**
*
* 备份数据库
* @return
* @throws Exception
*/
public static BackupParams backup(BackupParams backupParams) throws Exception {
File backupFolderFile = new File(backupParams.getBackupFolderPath());
if(!backupFolderFile.exists()) {
// 如果目录不存在则创建
backupFolderFile.mkdirs();
}
if(!backupParams.getBackupFolderPath().endsWith(File.separator) && !backupParams.getBackupFolderPath().endsWith("/")) {
if(systemLinuxFlag){
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + "/") ;
}else{
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + File.separator) ;
}
}
// 拼接执行命令
String backupFilePath = backupParams.getBackupFolderPath() + backupParams.getFileName();
backupParams.setBackupFolder(backupParams.getBackupFolderPath());
backupParams.setBackupFileName(backupParams.getFileName());
StringBuilder stringBuilder = new StringBuilder();
//MySQLdump在的目录,防止打不开
File file=new File("");
if(StringUtils.isNotBlank(backupParams.getOriginalBackupFileName())){
if(systemLinuxFlag){
stringBuilder.append("rm -f ");
}else{
stringBuilder.append("del /f/s/q ");
}
stringBuilder.append(backupParams.getBackupFolder()).append(backupParams.getOriginalBackupFileName()).append(" && ");
}
stringBuilder.append(file.getAbsolutePath()).append("\\");
stringBuilder.append("mysqldump ");
//端口号
stringBuilder.append(" -h").append(backupParams.getHost()).append(" -u").append(backupParams.getUserName()).append(" -p").append(backupParams.getPassword()).append(" -P").append(backupParams.getPort());
// 自定义备份表
if(backupParams.getTables() != null && backupParams.getTables().length > 0 ){
stringBuilder.append(" ").append(backupParams.getDatabase()).append(" --tables ");
List<String> tableList = Arrays.asList(backupParams.getTables());
for(int i=0;i< tableList.size();i++){
if(StringUtils.isNotBlank(tableList.get(i).replaceAll("\"",""))){
stringBuilder.append(tableList.get(i).replaceAll("\"",""));
}
if(i!=tableList.size()-1){
stringBuilder.append(" ");
}
}
}else{
stringBuilder.append(" ").append(backupParams.getDatabase());
}
// 添加--ssl-mode=DISABLED 防止出现 SSL 2026的错误
stringBuilder.append(" --ssl-mode=DISABLED");
stringBuilder.append(" --result-file=").append(backupFilePath).append(" --default-character-set=utf8 ");
// 调用外部执行 exe文件的Java API
System.out.println("MYSQL备份命令===============》 "+ String.valueOf(stringBuilder));
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(errorStream,"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
log.error("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
// 0 表示线程正常终止
log.info("数据完成备份,备份至 " + backupFilePath + " 文件中。");
// 替换insert into 为 replace into
try {
File replaceFile = new File(backupFilePath);
backupParams.setFileName(replaceSql(backupFilePath));
replaceFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
backupParams.setResultStatus(true);
return backupParams;
}
// backupParams.setResultStatus(true);
backupParams.setResultStatus(false);
return backupParams;
}
/**
* 增量备份时刷新数据库日志
* @param restoreParams
* @return
* @throws Exception
*/
public static boolean refreshLog(RestoreParams restoreParams) throws Exception {
StringBuilder stringBuilder = new StringBuilder();
if(systemLinuxFlag){
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" && ");
stringBuilder.append("./mysql -h").append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
stringBuilder.append(" -A -D ").append(restoreParams.getDatabase()).append(" -e \"SOURCE "+ restoreParams.getRestoreFilePath() +"\"");
}else {
stringBuilder.append(restoreParams.getMysqlPath().substring(0,restoreParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" & ");
stringBuilder.append("./mysqladmin -h").append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
stringBuilder.append(" flush-logs");
}
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
log.error("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
// 0 表示线程正常终止
log.info("数据库增量备份日志刷新成功!!!!");
return true;
}
return false;
// return true;
}
/**
* 恢复数据库
* @return
* @throws Exception
*/
public static RestoreParams restore(RestoreParams restoreParams) throws Exception {
StringBuilder stringBuilder = new StringBuilder();
// 全量恢复
if(restoreParams.getRestoreType() == 1){
// stringBuilder.append("C:\\Users\\panfei\\IdeaProjects\\train-software-server6\\train-software-serverdev\\bin\\") ;
File file=new File("");
// stringBuilder.append(file.getAbsolutePath()).append("\\");
if(systemLinuxFlag){
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" && ");
stringBuilder.append("./mysql -h").append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
stringBuilder.append(" -A -D ").append(restoreParams.getDatabase()).append(" -e \"SOURCE "+ restoreParams.getRestoreFilePath()+restoreParams.getFileName() +"\"");
}else {
stringBuilder.append(restoreParams.getMysqlPath().substring(0,restoreParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" & ");
stringBuilder.append("mysql -h").append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
stringBuilder.append(" -A -D ").append(restoreParams.getDatabase()).append(" -e \"SOURCE "+ restoreParams.getRestoreFilePath()+restoreParams.getFileName() +"\"");
}
try {
System.out.println("MYSQL数据全量恢复命令========> "+stringBuilder.toString());
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(errorStream,"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
System.out.println("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
log.info("数据已从 " + restoreParams.getRestoreFilePath() + restoreParams.getFileName() + " 导入至数据库中。");
}
} catch (Exception e) {
e.printStackTrace();
restoreParams.setResultStatus(false);
return restoreParams;
}
restoreParams.setResultStatus(true);
}else{
// 增量备份恢复
// 先执行第一次全量备份恢复
restoreParams.setRestoreType(1);
String fileName = restoreParams.getFileName();
restoreParams.setFileName("MYSQL_ADDBACKUP_FULL.sql");
restoreParams = restore(restoreParams);
restoreParams.setFileName(fileName);
restoreParams.setRestoreType(2);
if(restoreParams.isResultStatus()){
// 第一次全量备份恢复成功后获取增量备份文件夹下所有文件
File logfile = new File(restoreParams.getAddBackUpFilePath());
String logFileName = restoreParams.getAddBackUpFileName();
// 排除.index文件并按修改时间正序排序
List<File> fileList = Arrays.asList(logfile.listFiles()).stream().filter(p -> p.getName().contains(logFileName) && !p.getName().endsWith(".index")).sorted(Comparator.comparing(File::lastModified)).collect(Collectors.toList());
for(File file : fileList){
// 删除Mysql二进制日志命令:在bin目录下执行: mysql -uroot -proot testbackup -e "reset master;"
// 循环执行增量恢复脚本 mysqlbinlog --no-defaults D:\mysql-8.0.28-winx64\Data\mysql-bin.000012 |mysql -uroot -proot
StringBuilder newStringBuilder = new StringBuilder();
if(systemLinuxFlag){
newStringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" && ");
newStringBuilder.append("./mysqlbinlog --no-defaults ").append(file.getAbsolutePath()).append(" |mysql").append(" -h");
newStringBuilder.append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
}else {
newStringBuilder.append(restoreParams.getMysqlPath().substring(0,restoreParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
newStringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" & ");
newStringBuilder.append("mysqlbinlog --no-defaults ").append(file.getAbsolutePath()).append(" |mysql").append(" -h");
newStringBuilder.append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
}
try {
System.out.println("MYSQL增量备份数据恢复命令========> "+ newStringBuilder.toString());
Process process = Runtime.getRuntime().exec(getCommand(newStringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(errorStream,"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
System.out.println("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
log.info("数据已从 " + file.getAbsolutePath() + " 导入至数据库中。");
}
} catch (Exception e) {
e.printStackTrace();
restoreParams.setResultStatus(false);
return restoreParams;
}
// 循环到页面选择的恢复时间为止
if(file.getName().equals(restoreParams.getFileName())){
break;
}
}
restoreParams.setResultStatus(true);
}else{
restoreParams.setResultStatus(false);
}
return restoreParams;
}
restoreParams.setResultStatus(true);
return restoreParams;
}
private static String[] getCommand(String command) {
String os = System.getProperty("os.name");
String shell = "/bin/bash";
String c = "-c";
if(os.toLowerCase().startsWith("win")){
shell = "cmd.exe";
c = "/c";
}
String[] cmd = { shell, c, command};
return cmd;
}
/**
*
* 增量备份数据库
*
* 增量备份的思路是先进行一次全量备份 然后定时刷新生成的二进制日志 恢复时先全量恢复 然后按顺序增量恢复所有日志
* @return
* @throws Exception
*/
public static BackupParams addBackup(BackupParams backupParams) throws Exception {
// 首先全量备份一次
// String backupFolder = windowsBackupFolder +"\\addbackup";
if(systemLinuxFlag){
backupParams.setBackupFolder(backupParams.getBackupFolder()+ "/addbackup");
}else{
backupParams.setBackupFolder(backupParams.getBackupFolder()+ "\\addbackup");
}
// fileName 在增量恢复时要用到
//
String filename = backupParams.getFileName();
// backupParams.setFileName("MYSQL_ADDBACKUP_FULL.sql");
BackupParams backupParams1 = new BackupParams();
BeanUtils.copyProperties(backupParams,backupParams1);
backupParams1.setFileName("MYSQL_ADDBACKUP_FULL.sql");
backup(backupParams1);
// backupParams.setFileName(filename);
File backupFolderFile = new File(backupParams.getBackupFolderPath());
if(!backupFolderFile.exists()) {
// 如果目录不存在则创建
backupFolderFile.mkdirs();
}
if(!backupParams.getBackupFolderPath().endsWith(File.separator) && !backupParams.getBackupFolderPath().endsWith("/")) {
// backupParams.setBackupFolderPath(backupParams.getBackupFolderPath()+ File.separator) ;
if(systemLinuxFlag){
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + "/") ;
}else{
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + File.separator) ;
}
}
return addBackUpFlushLogs(backupParams);
}
/**
* 增量备份刷新二进制日志
* @return
* @throws IOException
* @throws InterruptedException
*/
public static BackupParams addBackUpFlushLogs(BackupParams backupParams) throws IOException, InterruptedException {
// 拼接执行命令
String backupFilePath = backupParams.getBackupFolderPath() ;
StringBuilder stringBuilder = new StringBuilder();
//MySQLdump在的目录,防止打不开
if(systemLinuxFlag){
stringBuilder.append("cd ").append(backupParams.getMysqlPath()).append(" && ");
stringBuilder.append("./mysqladmin ");
}else {
stringBuilder.append(backupParams.getMysqlPath().substring(0,backupParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(backupParams.getMysqlPath()).append(" & ");
stringBuilder.append("mysqladmin.exe ");
}
// //端口号
stringBuilder.append(" -h").append(backupParams.getHost()).append(" -u").append(backupParams.getUserName()).append(" -p").append(backupParams.getPassword()).append(" -P").append(backupParams.getPort());
stringBuilder.append(" flush-logs");
// stringBuilder.append(" --result-file=").append(backupFilePath).append("00001").append(" --default-character-set=utf8 ").append(database);
System.out.println("增量备份刷新二进制日志命令============》"+stringBuilder.toString());
// 调用外部执行 exe文件的Java API
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
log.error("输出值:" + process.waitFor());
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(errorStream,"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
if(process.waitFor() == 0) {
// 0 表示线程正常终止
log.info("数据完成备份,备份至 " + backupFilePath + " 文件中。");
backupParams.setResultStatus(true);
return backupParams;
}
backupParams.setResultStatus(false);
// backupParams.setResultStatus(true);
return backupParams;
}
/**
* 将MySQL备份脚本中的insert into 替换为 replace into
* @param fileName
* @throws Exception
*/
private static String replaceSql(String fileName) throws Exception {
BufferedReader br = null;
BufferedWriter bw = null;
String line = null;
StringBuffer buf = new StringBuffer();
InputStreamReader read = new InputStreamReader(new FileInputStream(new File(fileName)), "utf-8");
br = new BufferedReader(read);
String newFileName = fileName.substring(0,fileName.lastIndexOf(".")) + "replace"+ DateUtils.format(new Date(), BackupConstants.DATE_FORMAT) +".sql";
OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(new File(newFileName)),"utf-8");
bw = new BufferedWriter(write);
try {
while((line = br.readLine()) != null) {
line = line.replaceAll("INSERT INTO","REPLACE INTO");
buf.append(line.toString()).append("\n");
}
bw.write(buf.toString());
} catch (IOException e) {
e.printStackTrace();
}finally {
read.close();
write.flush();
bw.flush();
write.close();
bw.close();
}
return newFileName.substring(newFileName.lastIndexOf("\\")+1);
}
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 达梦数据库备份与恢复工具类
*/
@Slf4j
public class DamengBackupRestoreUtils {
/**
*
* 备份数据库
* @return
* @throws Exception
*/
public static BackupParams backup(BackupParams backupParams) throws Exception {
File backupFolderFile = new File(backupParams.getBackupFolderPath());
if(!backupFolderFile.exists()) {
// 如果目录不存在则创建
backupFolderFile.mkdirs();
}
if(!backupParams.getBackupFolderPath().endsWith(File.separator) && !backupParams.getBackupFolderPath().endsWith("/")) {
if(systemLinuxFlag){
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + "/") ;
}else{
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + File.separator) ;
}
}
// 拼接执行命令
String backupFilePath = backupParams.getBackupFolderPath() + backupParams.getFileName();
backupParams.setBackupFolder(backupParams.getBackupFolderPath());
backupParams.setBackupFileName(backupParams.getFileName());
StringBuilder stringBuilder = new StringBuilder();
//DAMENGdump在的目录,防止打不开
// 先删除上一次备份的文件 del /f/s/q C:\disk\dateBackup\20230220175325copy20230220175326.sql
if(StringUtils.isNotBlank(backupParams.getOriginalBackupFileName()) && backupParams.getBackupType() != 2){
if(systemLinuxFlag){
stringBuilder.append("rm -f ");
}else{
stringBuilder.append("del /f/s/q ");
}
stringBuilder.append(backupParams.getBackupFolder()).append(backupParams.getOriginalBackupFileName()).append(" && ");
}
if(!systemLinuxFlag){
stringBuilder.append(backupParams.getMysqlPath().substring(0,backupParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(backupParams.getMysqlPath()).append(" & ");
}else{
stringBuilder.append("./");
stringBuilder.append("cd ").append(backupParams.getMysqlPath()).append(" && ");
}
stringBuilder.append("dexp ");
// 用户名 密码 ip 端口号 模式名/密码@IP:5236 file=dexp_tab01.dmp log=dexp_tab01.log tables=表名
stringBuilder.append(backupParams.getUserName()).append("/").append(backupParams.getPassword()).append("@").append(backupParams.getHost()).append(":").append(backupParams.getPort());
// 备份文件名
stringBuilder.append(" file=").append(backupFilePath);
// 备份的日志文件名
stringBuilder.append(" log=").append(backupFilePath.replace(BackupConstants.DAMENG_EXT,".log"));
// 自定义备份表
if(backupParams.getTables() != null && backupParams.getTables().length > 0 ){
stringBuilder.append(" ").append(" tables=");
List<String> tableList = Arrays.asList(backupParams.getTables());
for(int i=0;i< tableList.size();i++){
if(StringUtils.isNotBlank(tableList.get(i).replaceAll("\"",""))){
stringBuilder.append(tableList.get(i).replaceAll("\"",""));
}
if(i!=tableList.size()-1){
stringBuilder.append(",");
}
}
}
// stringBuilder.append(";");
// 调用外部执行 exe文件的Java API
System.out.println("DAMENG备份命令===============》 "+ String.valueOf(stringBuilder));
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
// FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(process.getInputStream(),"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
log.error("输出值:" + process.waitFor());
// 成功终止导出, 但出现警告
//2023-02-27 11:09:48.692 [ERROR] - 输出值:2
if(process.waitFor() == 0 || process.waitFor() == 2) {
// 0 表示线程正常终止
log.info("数据完成备份,备份至 " + backupFilePath + " 文件中。");
backupParams.setResultStatus(true);
return backupParams;
}
backupParams.setResultStatus(false);
return backupParams;
}
/**
* 增量备份时刷新数据库日志
* @param restoreParams
* @return
* @throws Exception
*/
public static boolean refreshLog(RestoreParams restoreParams) throws Exception {
StringBuilder stringBuilder = new StringBuilder();
if(systemLinuxFlag){
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" && ");
stringBuilder.append("./DAMENG -h").append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
stringBuilder.append(" -A -D ").append(restoreParams.getDatabase()).append(" -e \"SOURCE "+ restoreParams.getRestoreFilePath() +"\"");
}else {
stringBuilder.append(restoreParams.getMysqlPath().substring(0,restoreParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" & ");
stringBuilder.append("DAMENGadmin -h").append(restoreParams.getHost()).append(" -u").append(restoreParams.getUserName()).append(" -p").append(restoreParams.getPassword()).append(" -P").append(restoreParams.getPort());
stringBuilder.append(" flush-logs");
}
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
log.error("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
// 0 表示线程正常终止
log.info("数据库增量备份日志刷新成功!!!!");
return true;
}
return false;
// return true;
}
/**
* 恢复数据库
* @return
* @throws Exception
*/
public static RestoreParams restore(RestoreParams restoreParams) throws Exception {
StringBuilder stringBuilder = new StringBuilder();
// 全量恢复
if(restoreParams.getRestoreType() == 1){
// dimp 模式/密码@ip:5236 file=dexp_tab01.dmp log=dexp_tab01.log tables=表名 TABLE_EXISTS_ACTION=TRUNCATE
if(systemLinuxFlag){
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" && ");
stringBuilder.append("./");
}else {
stringBuilder.append(restoreParams.getMysqlPath().substring(0,restoreParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" & ");
}
stringBuilder.append("dimp ");
// 用户名 密码 ip 端口号 dexp 模式/密码@ip:5236 file=dexp_tab01.dmp log=dexp_tab01.log tables=xl_user
stringBuilder.append(restoreParams.getUserName()).append("/").append(restoreParams.getPassword()).append("@").append(restoreParams.getHost()).append(":").append(restoreParams.getPort());
stringBuilder.append(" file=").append(restoreParams.getRestoreFilePath()+restoreParams.getFileName());
stringBuilder.append(" log=").append("restore_"+(new SimpleDateFormat(BackupConstants.DATE_FORMAT)).format(new Date())+".log");
// 自定义备份表
if(restoreParams.getTables() != null && restoreParams.getTables().length > 0 ){
stringBuilder.append(" ").append(" tables=");
List<String> tableList = Arrays.asList(restoreParams.getTables());
for(int i=0;i< tableList.size();i++){
if(StringUtils.isNotBlank(tableList.get(i).replaceAll("\"",""))){
stringBuilder.append(tableList.get(i).replaceAll("\"",""));
}
if(i!=tableList.size()-1){
stringBuilder.append(",");
}
}
}
// TABLE_EXISTS_ACTION=TRUNCATE 表示表存在时先删除表
stringBuilder.append(" TABLE_EXISTS_ACTION=TRUNCATE");
try {
System.out.println("DAMENG数据全量恢复命令========> "+stringBuilder.toString());
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(process.getInputStream(),"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
System.out.println("输出值:" + process.waitFor());
//
if(process.waitFor() == 0 || process.waitFor() == 2) {
log.info("数据已从 " + restoreParams.getRestoreFilePath() + restoreParams.getFileName() + " 导入至数据库中。");
}
} catch (Exception e) {
e.printStackTrace();
restoreParams.setResultStatus(false);
return restoreParams;
}
restoreParams.setResultStatus(true);
}else{
// 增量备份恢复
// 先执行第一次全量备份恢复
String fileName = restoreParams.getFileName();
RestoreParams restoreParams1 = new RestoreParams();
BeanUtils.copyProperties(restoreParams,restoreParams1);
restoreParams1.setRestoreType(1);
restoreParams1.setFileName("DAMENG_ADDBACKUP_FULL.dmp");
restoreParams1 = restore(restoreParams1);
if(restoreParams1.isResultStatus()){
// 第一次全量备份恢复成功后获取增量备份文件夹下所有文件
File logfile = new File(restoreParams.getRestoreFilePath());
// 按修改时间正序排序
List<File> fileList = Arrays.asList(logfile.listFiles()).stream().filter(p -> !p.getName().contains("DAMENG_ADDBACKUP_FULL.dmp") && !p.getName().endsWith(".log")).sorted(Comparator.comparing(File::lastModified)).collect(Collectors.toList());
for(File file : fileList){
// 删除DAMENG二进制日志命令:在bin目录下执行: DAMENG -uroot -proot testbackup -e "reset master;"
// 循环执行增量恢复脚本 DAMENGbinlog --no-defaults D:\DAMENG-8.0.28-winx64\Data\DAMENG-bin.000012 |DAMENG -uroot -proot
StringBuilder newStringBuilder = new StringBuilder();
if(systemLinuxFlag){
newStringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" && ");
newStringBuilder.append("./");
}else {
newStringBuilder.append(restoreParams.getMysqlPath().substring(0,restoreParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
newStringBuilder.append("cd ").append(restoreParams.getMysqlPath()).append(" & ");
}
newStringBuilder.append("dimp ");
// 用户名 密码 ip 端口号 dexp 模式/密码@ip:5236 file=dexp_tab01.dmp log=dexp_tab01.log tables=表名
newStringBuilder.append(restoreParams.getUserName()).append("/").append(restoreParams.getPassword()).append("@").append(restoreParams.getHost()).append(":").append(restoreParams.getPort());
newStringBuilder.append(" file=").append(file.getAbsolutePath());
newStringBuilder.append(" log=").append("restore_"+(new SimpleDateFormat(BackupConstants.DATE_FORMAT)).format(new Date())+".log");
// 自定义备份表
if(restoreParams.getTables() != null && restoreParams.getTables().length > 0 ){
newStringBuilder.append(" ").append(" tables=");
List<String> tableList = Arrays.asList(restoreParams.getTables());
for(int i=0;i< tableList.size();i++){
if(StringUtils.isNotBlank(tableList.get(i).replaceAll("\"",""))){
newStringBuilder.append(tableList.get(i).replaceAll("\"",""));
}
if(i!=tableList.size()-1){
newStringBuilder.append(",");
}
}
}
// TABLE_EXISTS_ACTION=APPEND 表示表存在时追加数据
newStringBuilder.append(" TABLE_EXISTS_ACTION=APPEND");
// PRIMARY_CONFLICT:主键冲突时的处理方式: IGNORE|OVERWRITE 默认报错
newStringBuilder.append(" PRIMARY_CONFLICT=IGNORE ");
try {
System.out.println("DAMENG增量备份数据恢复命令========> "+ newStringBuilder.toString());
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(process.getInputStream(),"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
System.out.println("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
log.info("数据已从 " + file.getAbsolutePath() + " 导入至数据库中。");
}
} catch (Exception e) {
e.printStackTrace();
restoreParams.setResultStatus(false);
return restoreParams;
}
// 循环到页面选择的恢复时间为止
if(file.getName().equals(restoreParams.getFileName())){
break;
}
}
restoreParams.setResultStatus(true);
}else{
restoreParams.setResultStatus(false);
}
return restoreParams;
}
restoreParams.setResultStatus(true);
return restoreParams;
}
private static String[] getCommand(String command) {
// String os = System.getProperty("os.name");
String shell = "/bin/bash";
String c = "-c";
if(!systemLinuxFlag){
shell = "cmd.exe";
c = "/c";
}
String[] cmd = { shell, c, command};
return cmd;
}
/**
*
* 增量备份数据库
*
* 增量备份的思路是先进行一次全量备份 然后定时备份新的 恢复时先全量恢复 然后按顺序增量恢复所有备份文件
* @return
* @throws Exception
*/
public static BackupParams addBackup(BackupParams backupParams) throws Exception {
// fileName 在增量恢复时要用到
String filename = backupParams.getFileName();
if(backupParams.isFirstBackUpFlag()){
backupParams.setFileName("DAMENG_ADDBACKUP_FULL.dmp");
// 首次增量备份要先全量备份一次
backup(backupParams);
backupParams.setBackupFileName("DAMENG_ADDBACKUP_FULL.dmp");
}else{
backupParams.setFileName(filename);
File backupFolderFile = new File(backupParams.getBackupFolderPath());
if(!backupFolderFile.exists()) {
// 如果目录不存在则创建
backupFolderFile.mkdirs();
}
if(!backupParams.getBackupFolderPath().endsWith(File.separator) && !backupParams.getBackupFolderPath().endsWith("/")) {
// backupParams.setBackupFolderPath(backupParams.getBackupFolderPath()+ File.separator) ;
if(systemLinuxFlag){
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + "/") ;
}else{
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + File.separator) ;
}
}
addBackUpFlushLogs(backupParams) ;
}
return backupParams;
}
/**
* 增量备份
* @return
* @throws IOException
* @throws InterruptedException
*/
private static BackupParams addBackUpFlushLogs(BackupParams backupParams) throws IOException, InterruptedException {
// 拼接执行命令
String backupFilePath = backupParams.getBackupFolderPath()+backupParams.getFileName() ;
StringBuilder stringBuilder = new StringBuilder();
if(!systemLinuxFlag){
stringBuilder.append(backupParams.getMysqlPath().substring(0,backupParams.getMysqlPath().indexOf(":")).toLowerCase()).append(":").append(" && ");
stringBuilder.append("cd ").append(backupParams.getMysqlPath()).append(" & ");
stringBuilder.append("dexp ");
}else {
stringBuilder.append("cd ").append(backupParams.getMysqlPath()).append(" && ");
stringBuilder.append("./dexp ");
}
// 用户名 密码 ip 端口号 dexp 模式/密码@ip:5236 file=dexp_tab01.dmp log=dexp_tab01.log tables=表名
stringBuilder.append(backupParams.getUserName()).append("/").append(backupParams.getPassword()).append("@").append(backupParams.getHost()).append(":").append(backupParams.getPort());
// 备份文件名
stringBuilder.append(" file=").append(backupFilePath);
// 备份的日志文件名
stringBuilder.append(" log=").append(backupFilePath.replace(BackupConstants.DAMENG_EXT,".log"));
// 自定义备份表
if( backupParams.getTables() != null && backupParams.getTables().length > 0 ){
stringBuilder.append(" ").append(" tables=");
List<String> tableList = Arrays.asList(backupParams.getTables());
for(int i=0;i< tableList.size();i++){
if(StringUtils.isNotBlank(tableList.get(i).replaceAll("\"",""))){
stringBuilder.append(tableList.get(i).replaceAll("\"",""));
}
if(i!=tableList.size()-1){
stringBuilder.append(",");
}
}
}
// 拼接上次执行时间之后的where条件
if(backupParams.getLastTime() != null){
// QUERY="where created >= to_date('2022-02-20 00:00:00','yyyy-mm-dd hh24:mi:ss')"
// 为防止数据丢失 每次增量备份时间往前提前30分钟 2023-02-27 20:19:03
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String backUpTime = sdf.format(DateUtils.addMinute(sdf.parse(backupParams.getLastTime()),-30));
if(backupParams.getTables() == null || backupParams.getTables().length == 0){
stringBuilder.append(" ").append(" tables=").append(backupParams.getAllTables());
}
stringBuilder.append(" QUERY=\"where created >= to_date('").append(backUpTime).append("','yyyy-mm-dd hh24:mi:ss')\"");
} catch (ParseException e) {
e.printStackTrace();
}
}
log.info("达梦数据库增量备份命令:"+stringBuilder.toString());
// 调用外部执行 exe文件的Java API
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
// FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(process.getInputStream(),"gbk");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
log.error("输出值:" + process.waitFor());
if(process.waitFor() == 0 || process.waitFor() == 2) {
// 0 表示线程正常终止
log.info("数据完成备份,备份至 " + backupFilePath + " 文件中。");
backupParams.setResultStatus(true);
return backupParams;
}
backupParams.setResultStatus(false);
return backupParams;
}
/**
* 将DAMENG备份脚本中的insert into 替换为 replace into
* @param fileName
* @throws Exception
*/
private static String replaceSql(String fileName) throws Exception {
BufferedReader br = null;
BufferedWriter bw = null;
String line = null;
StringBuffer buf = new StringBuffer();
InputStreamReader read = new InputStreamReader(new FileInputStream(new File(fileName)), "utf-8");
br = new BufferedReader(read);
String newFileName = fileName.substring(0,fileName.lastIndexOf(".")) + "replace"+ DateUtils.format(new Date(), BackupConstants.DATE_FORMAT) +".sql";
OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(new File(newFileName)),"utf-8");
bw = new BufferedWriter(write);
try {
while((line = br.readLine()) != null) {
line = line.replaceAll("INSERT INTO","REPLACE INTO");
buf.append(line.toString()).append("\n");
}
bw.write(buf.toString());
} catch (IOException e) {
e.printStackTrace();
}finally {
read.close();
write.flush();
bw.flush();
write.close();
bw.close();
}
return newFileName.substring(newFileName.lastIndexOf("\\")+1);
}
}
mySqlBackupService.restoreBackUp
@Override
public boolean restoreBackUp(String id, String fileName) throws Exception {
//ip
String host = properties.getHost();
//数据库用户名
String userName = properties.getUserName();
//数据库密码
String password = properties.getPassword();
//数据库名称
String database = properties.getDatabase();
String port=properties.getPort();
BackupDO backupDO = backupMapper.selectById(id);
String mysqlPath = winMysqlPath;
if(systemLinuxFlag){
mysqlPath = linuxMysqlPath;
}
DictVo dict = dictService.selectById(backupDO.getBackupTypeId());
int restoreType = 1;
if(dict != null){
if(dict.getDictItem().contains("全量")){
restoreType = 1;
}else if(dict.getDictItem().contains("增量")){
restoreType = 2;
}
}
RestoreParams restoreParams = new RestoreParams();
restoreParams.setRestoreFilePath(backupDO.getSavePath());
restoreParams.setHost(host);
restoreParams.setUserName(userName);
restoreParams.setPassword(password);
restoreParams.setPort(port);
restoreParams.setFileName(backupDO.getFileName());
if(restoreType == 2 && fileName!= null){
// 增量恢复时恢复到选择的文件名
restoreParams.setFileName(fileName);
}
restoreParams.setDatabase(database);
restoreParams.setMysqlPath(mysqlPath);
restoreParams.setRestoreType(restoreType);
// mysql增量备份的地址为日志文件夹
restoreParams.setAddBackUpFilePath(mysqlbinlogpath);
restoreParams.setAddBackUpFileName(addBackupLogName);
// 恢复数据库
restoreParams = mySQLBackupService.restore(restoreParams);
// 恢复上传文件
if(restoreParams.isResultStatus()){
restoreParams.setFileName(backupDO.getUploadFileSaveName());
restoreParams.setAddBackUpFilePath(backupDO.getUploadFileSavePath());
restoreParams = filesBackUpService.restore(restoreParams);
}
// // 全部恢复完之后将状态改为已恢复
// backupMapper.updateStatusById(backupDO.getId(),3);
// 更新上次备份时间
backupMapper.updateLastRestoreTime(backupDO.getId(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return restoreParams.isResultStatus();
}
damengBackupService.restoreBackUp
@Override
public boolean restoreBackUp(String id, String fileName) throws Exception {
//ip
String host = properties.getHost();
//数据库用户名
String userName = properties.getUserName();
//数据库密码
String password = properties.getPassword();
//数据库名称
String database = properties.getDatabase();
String port=properties.getPort();
BackupDO backupDO = backupMapper.selectById(id);
DictVo dict = dictService.selectById(backupDO.getBackupTypeId());
int restoreType = 1;
if(dict != null){
if(dict.getDictItem().contains("全量")){
restoreType = 1;
}else if(dict.getDictItem().contains("增量")){
restoreType = 2;
}
}
String mysqlPath = winMysqlPath;
if(systemLinuxFlag){
mysqlPath = linuxMysqlPath;
}
RestoreParams restoreParams = new RestoreParams();
restoreParams.setRestoreFilePath(backupDO.getSavePath());
restoreParams.setHost(host);
restoreParams.setUserName(userName);
restoreParams.setPassword(password);
restoreParams.setPort(port);
restoreParams.setFileName(backupDO.getFileName());
if(restoreType == 2 && fileName!= null){
// 增量恢复时恢复到选择的文件名
restoreParams.setFileName(fileName);
}
restoreParams.setDatabase(database);
restoreParams.setMysqlPath(mysqlPath);
restoreParams.setRestoreType(restoreType);
if(com.jsrd.trainsoftwareserver.common.untils.StringUtils.isNotBlank(backupDO.getTables())){
restoreParams.setTables(backupDO.getTables().split(","));
}
// 恢复数据库
restoreParams = daMengBackupService.restore(restoreParams);
if(restoreParams.isResultStatus()){
// 恢复上传文件
restoreParams.setFileName(backupDO.getUploadFileSaveName());
restoreParams.setAddBackUpFilePath(backupDO.getUploadFileSavePath());
restoreParams = filesBackUpService.restore(restoreParams);
}
// 全部恢复完之后将状态改为已恢复
// backupMapper.updateStatusById(backupDO.getId(),3);
// 更新上次备份时间
backupMapper.updateLastRestoreTime(backupDO.getId(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return restoreParams.isResultStatus();
}
filesBackUpService.backup
/**
* 备份上传的文件
* @param backupParams
* @return
* @throws Exception
*/
@Override
public BackupParams backup(BackupParams backupParams) throws Exception {
if(systemLinuxFlag){
backupParams.setBackupFolderPath(uploadPathLinux);
backupParams.setBackupFolder(backupPathLinux);
}else{
backupParams.setBackupFolderPath(uploadPathWindows);
backupParams.setBackupFolder(backupPathWindows);
}
// 压缩包文件名
backupParams.setFileName(BackupConstants.DEFAULT_BACKUP_PRE + DateUtils.format(new Date(), BackupConstants.DATE_FORMAT) + BackupConstants.DEFAULT_BACKUP_SUFFIX);
return UploadFileBackupRestoreUtils.backup(backupParams);
}
filesBackUpService.restore
@Override
public RestoreParams restore(RestoreParams restoreParams) throws Exception {
if(systemLinuxFlag){
restoreParams.setRestoreFilePath(uploadPathLinux);
}else{
restoreParams.setRestoreFilePath(uploadPathWindows);
}
restoreParams.setPanFu(panFu);
return UploadFileBackupRestoreUtils.restore(restoreParams);
}
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
/**
* 备份与恢复文件资源
*/
@Slf4j
public class UploadFileBackupRestoreUtils {
/**
* 备份上传文件
* @param backupParams
* @return
* @throws Exception
*/
public static BackupParams backup(BackupParams backupParams) throws Exception {
File backupFolderFile = new File(backupParams.getBackupFolder());
if(!backupFolderFile.exists()) {
// 如果目录不存在则创建
backupFolderFile.mkdirs();
}
// 拼接执行命令
String backupFilePath = backupParams.getBackupFolder() + backupParams.getFileName();
if(systemLinuxFlag){
if(!backupParams.getBackupFolder().endsWith(File.separator) && !backupParams.getBackupFolder().endsWith("/")) {
backupParams.setBackupFolderPath(backupParams.getBackupFolderPath() + "/") ;
backupParams.setBackupFolder(backupParams.getBackupFolder() + "/");
backupFilePath = backupParams.getBackupFolder() + backupParams.getFileName();
}
}else{
if(!backupParams.getBackupFolder().endsWith(File.separator) && !backupParams.getBackupFolder().endsWith("\\")){
backupParams.setBackupFolder(backupParams.getBackupFolder() + "\\") ;
}
backupFilePath = backupParams.getBackupFolder() + backupParams.getFileName();
}
backupParams.setBackupFileName(backupParams.getFileName());
StringBuilder stringBuilder = new StringBuilder();
if(StringUtils.isNotBlank(backupParams.getOriginalBackupFileName())){
// 先删除上一次备份的文件 del /f/s/q C:\disk\dateBackup\20230220175325copy20230220175326.sql
if(systemLinuxFlag){
stringBuilder.append("rm -f ");
}else{
stringBuilder.append("del /f/s/q ");
}
stringBuilder.append(backupParams.getBackupFolder()).append(backupParams.getOriginalBackupFileName()).append(" && ");
}
stringBuilder.append("tar");
//端口号
stringBuilder.append(" -czvf ").append(backupParams.getBackupFolder() + backupParams.getFileName()).append(" ").append(backupParams.getBackupFolderPath());
// 调用外部执行 exe文件的Java API
System.out.println("上传文件压缩备份命令===============》 "+ String.valueOf(stringBuilder));
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(errorStream,"utf-8");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
log.error("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
// 0 表示线程正常终止
log.info("数据完成备份,备份至 " + backupFilePath + " 文件中。");
backupParams.setResultStatus(true);
return backupParams;
}
backupParams.setResultStatus(false);
// backupParams.setResultStatus(true);
return backupParams;
}
/**
* 恢复上传文件
* @param restoreParams
* @return
* @throws Exception
*/
public static RestoreParams restore(RestoreParams restoreParams) throws Exception {
StringBuilder stringBuilder = new StringBuilder();
// 先删除文件夹所有文件
if(systemLinuxFlag){
stringBuilder.append("rm -rf ").append(restoreParams.getRestoreFilePath()).append(" && ");
}else{
stringBuilder.append("rd/s/q ").append(restoreParams.getRestoreFilePath()).append(" && ");
}
// 再创建文件夹
if(systemLinuxFlag){
// 先创建文件夹
File file = new File(restoreParams.getRestoreFilePath());
if(!file.exists()){
file.mkdirs();
}
}
// 解压缩
stringBuilder.append("tar");
//端口号
stringBuilder.append(" -zxvf ").append(restoreParams.getAddBackUpFilePath()).append(restoreParams.getFileName());
// -C 是将同名文件覆盖并去掉路径信息
stringBuilder.append(" -C ");
if(!systemLinuxFlag){
stringBuilder.append(restoreParams.getPanFu());
}else{
// Linux 解压到根目录 自带文件路径
stringBuilder.append("/");
}
try {
System.out.println("上传文件解压缩恢复命令========> "+stringBuilder.toString());
Process process = Runtime.getRuntime().exec(getCommand(stringBuilder.toString()));
FileInputStream errorStream = (FileInputStream) process.getErrorStream();
// 读取错误信息
InputStreamReader isr = new InputStreamReader(errorStream,"GBK");
System.out.println(isr.getEncoding());
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while ((line = bufr.readLine()) != null){
System.out.println(line);
}
isr.close();
bufr.close();
System.out.println("输出值:" + process.waitFor());
if(process.waitFor() == 0) {
log.info("上传文件已恢复至 " + restoreParams.getRestoreFilePath() );
}
} catch (Exception e) {
e.printStackTrace();
restoreParams.setResultStatus(false);
return restoreParams;
}
restoreParams.setResultStatus(true);
return restoreParams;
}
private static String[] getCommand(String command) {
String os = System.getProperty("os.name");
String shell = "/bin/bash";
String c = "-c";
if(os.toLowerCase().startsWith("win")){
shell = "cmd.exe";
c = "/c";
}
String[] cmd = { shell, c, command};
return cmd;
}
}