MySQL Backup Restore

MySQL数据库的备份与恢复, 项目中需要对系统数据进行备份和恢复, 要求能在界面上点击进行自主备份和恢复, 所以有了这篇文章, 我把其中不涉及业务的部分摘了出来, 做了一下整理, 全部源代码在https://gitee.com/nichenxyx/mysql-backup-restore

先介绍一下整体思路

在使用JDBC去做系统数据的整体备份和删除是不太好做的, 更何况封装后的JPA呢, 所以使用原生的方式反而更方便

我在MySQL-5.7.22版本下将mysql命令程序和mysqldump命令程序直接拷贝了出来, 作为程序备份和恢复的基础工具并写了两个脚本执行相关业务逻辑的东西

下面说一下两个脚本,最后再说一下Java的逻辑

mysql-backup.sh -- 备份脚本

用法

-h host MySQL所在主机

-p port (可选参数)MySQL的端口, 默认3306

-u user 连接MySQL的用户

-P password 连接MySQL的密码

-d database 需要备份的数据库

-n ignore tables (可选参数)需要忽略的表, 如果有多个, 使用英文逗号分隔

示例

./mysql-bakup.sh -h 192.168.1.2 -p 3306 -u root -P password -d aa -n menu,role

结果

会以当前日期生成一个-finished.tar.gz结尾的文件

全部源代码

#!/bin/bash

# desc: 备份数据库

# author: zhuliang

whilegetopts 'h:p:u:P:d:n' OPT; do

case $OPT in

h)

host="$OPTARG";;

p)

port="$OPTARG";;

u)

user="$OPTARG";;

P)

password="$OPTARG";;

d)

database="$OPTARG";;

n)

ignoreTables="$OPTARG";;

?)

echo "$OPTARG"

echo -e "Usage:`basename $0`[options] optionsValue\n-h host\n-p port\n-u user\n-P password\n-d database\n-n ignore tables, optional, if has multiple value, split with\",\""

exit0

esac

done

if [[ -z $host ]]; then

echo "-h parameter missed, for help add -h parameter"

exit1

fi

if [[ -z $user ]]; then

echo "-u parameter missed, for help add -h parameter"

exit3

fi

if [[ -z $password ]]; then

echo "-P parameter missed, for help add -h parameter"

exit4

fi

if [[ -z $database ]]; then

echo "-d parameter missed, for help add -h parameter"

exit5

fi

if [[ -z $port ]]; then

port=3306

fi

# 进入命令所在目录

cd `dirname $0`

chmod +x ./mysqldump

# 拼接需要执行的命令

cmd="./mysqldump --host=$host--port=$port--user=$user--password=$password--databases$database--allow-keywords --compress --force --hex-blob --quick"

ignoreArray=(${ignoreTables//,/})

fortable in ${ignoreArray[@]}

do

cmd="$cmd--ignore-table=$database.$table"

done

echo $cmd

date=`date +"%Y%m%d%H%M%S"`

logfile=backup.log

date +"%Y-%m-%d %H:%M:%S Start backup database..." >> $logfile

sqlFile=$date.sql

tarFile=$date.tar.gz

# 执行命令并将结果保存

$cmd > $sqlFile

tarzcvf $tarFile $sqlFile

rm -rf $sqlFile

# 最终以finished.tar.gz结尾才算是最终备份成功

mv $tarFile $date-finished.tar.gz

date +"%Y-%m-%d %H:%M:%S Finished backup database" >> $logfile

mysql-restore.sh -- 恢复脚本

-h host MySQL所在主机

-p port (可选参数)MySQL的端口, 默认3306

-u user 连接MySQL的用户

-P password 连接MySQL的密码

-d database 需要恢复的数据库

-f sql tar file 需要用于恢复的压缩文件

示例

./mysql-restore.sh -h 192.168.1.2 -p 3306 -u root -P password -d aa -f20191111062402-finished.tar.gz

源代码

#!/bin/bash

# desc: 恢复数据

# author: zhuliang

whilegetopts 'h:p:u:P:d:f:' OPT; do

case $OPT in

h)

host="$OPTARG";;

p)

port="$OPTARG";;

u)

user="$OPTARG";;

P)

password="$OPTARG";;

d)

database="$OPTARG";;

f)

sqlTarFile="$OPTARG";;

?)

echo -e "Usage:`basename $0`[options] optionsValue\n-h host\n-p port\n-u user\n-P password\n-d database\n-f sql tar file"

exit0

esac

done

if [[ -z $host ]]; then

echo "-h parameter missed, for help add -h parameter"

exit1

fi

if [[ -z $user ]]; then

echo "-u parameter missed, for help add -h parameter"

exit3

fi

if [[ -z $password ]]; then

echo "-P parameter missed, for help add -h parameter"

exit4

fi

if [[ -z $database ]]; then

echo "-d parameter missed, for help add -h parameter"

exit5

fi

if [[ -z $sqlTarFile ]]; then

echo "-f parameter missed, for help add -h parameter"

exit6

fi

if [[ -z $port ]]; then

port=3306

fi

cd `dirname $0`

date=`date +"%Y%m%d-%H%M%S"`

logfile=backup.log

date +"%Y-%m-%d %H:%M:%S Start restore data..." >> $logfile

sqlFile=`uuidgen`.sql

tarzxvf $sqlTarFile -O > $sqlFile

chmod +x ./mysql

./mysql --host=$host --port=$port --user=$user --password=$password $database < $sqlFile

rm -rf $sqlFile

date +"%Y-%m-%d %H:%M:%S Finished restore data" >> $logfile

BackupRestoreService --Java调用

先看一下代码

package com.vhengdata.service;

import cn.hutool.core.collection.CollUtil;

import cn.hutool.core.io.FileUtil;

import cn.hutool.core.io.IoUtil;

import cn.hutool.core.util.StrUtil;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.core.io.ClassPathResource;

import org.springframework.stereotype.Service;

import java.io.*;

import java.sql.*;

import java.util.*;

import java.util.regex.Pattern;

import java.util.stream.Collectors;

@Service

@Slf4j

public class BackupRestoreService {

private final String BACKUP_POSTFIX = "-finished.tar.gz";

private volatile boolean backuping = false;

private volatile boolean restoring = false;

@Value("${spring.datasource.url:}")

private String jdbcUrl;

@Value("${spring.datasource.username:}")

private String username;

@Value("${spring.datasource.password:}")

private String password;

public List getBackups() {

String backupPath = "backup";

File backupFolder = new File(backupPath);

String[] backups = backupFolder.list(new FilenameFilter() {

@Override

public boolean accept(File dir, String name) {

return name.endsWith(BACKUP_POSTFIX);

}

});

List backList = CollUtil.toList(backups).stream()

.map(filename -> filename.replace(BACKUP_POSTFIX, ""))

.sorted((b1, b2) -> b2.compareToIgnoreCase(b1))

.collect(Collectors.toList());

return backList;

}

public void backupData() {

if (backuping) {

throw new ServiceException(ResultEnum.TASK_BASY.getCode(), "当前正在进行备份操作, 请稍后再试");

}

if (restoring) {

throw new ServiceException(ResultEnum.TASK_BASY.getCode(), "当前正在进行数据恢复操作, 请稍后再试");

}

try {

backuping = true;

String configBackupFolder = fileConfig.getBackup();

File configBackupFolderFile = new File(configBackupFolder);

if (!configBackupFolderFile.exists()) {

configBackupFolderFile.mkdirs();

}

// 获取参数

if (StrUtil.isEmpty(jdbcUrl)) {

throw new ServiceException(ResultEnum.PARAM_DATA_ERROR.getCode(), "数据库配置不正确");

}

String host, port, db, user, pwd;

Map dbConfigMap = readConfigFromJdbcUrl(jdbcUrl);

host = dbConfigMap.get("host");

port = dbConfigMap.get("port");

db = dbConfigMap.get("db");

if (dbConfigMap.containsKey("user")) {

user = dbConfigMap.get("user");

} else {

user = username;

}

if (dbConfigMap.containsKey("password")) {

pwd = dbConfigMap.get("password");

} else {

pwd = password;

}

String shellpath = configBackupFolder + "/mysql-bak.sh";

File shellFile = new File(shellpath);

if (!FileUtil.exist(shellFile)) {

InputStream inputStream = new ClassPathResource("mysql/mysql-bak.sh").getInputStream();

FileOutputStream outputStream = new FileOutputStream(shellFile);

IoUtil.copy(inputStream, outputStream);

inputStream.close();

outputStream.close();

}

String mysqlpath = configBackupFolder + "/mysqldump";

if (!FileUtil.exist(mysqlpath)) {

InputStream inputStream = new ClassPathResource("mysql/mysqldump").getInputStream();

FileOutputStream outputStream = new FileOutputStream(new File(mysqlpath));

IoUtil.copy(inputStream, outputStream);

inputStream.close();

outputStream.close();

}

if (!shellFile.canExecute()) {

shellFile.setExecutable(true);

}

String shellCmd = String.format("%s -h %s -p %s -u %s -P %s -d %s", shellFile.getAbsolutePath(), host, port, user, pwd, db);

log.info("Backup: {}", shellCmd);

Runtime.getRuntime().exec(shellCmd).waitFor();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

backuping = false;

}

}

public void restoreData(String filename) {

if (backuping) {

throw new ServiceException(ResultEnum.TASK_BASY.getCode(), "当前正在进行备份操作, 请稍后再试");

}

if (restoring) {

throw new ServiceException(ResultEnum.TASK_BASY.getCode(), "当前正在进行数据恢复操作, 请稍后再试");

}

try {

restoring = true;

if (!filename.endsWith(BACKUP_POSTFIX)) {

filename += BACKUP_POSTFIX;

}

String configBackupFolder = fileConfig.getBackup();

File configBackupFolderFile = new File(configBackupFolder);

if (!configBackupFolderFile.exists()) {

configBackupFolderFile.mkdirs();

}

String backupFilePath = configBackupFolder + "/" + filename;

File backupFile = new File(backupFilePath);

if (!backupFile.exists()) {

throw new TourException(ResultEnum.PARAM_ERROR.getCode(), "备份文件不存在");

}

String backupFileAbPath = backupFile.getAbsolutePath();

// 获取参数

if (StrUtil.isEmpty(jdbcUrl)) {

throw new TourException(ResultEnum.PARAM_DATA_ERROR.getCode(), "数据库配置不正确");

}

String host, port, db, user, pwd;

Map dbConfigMap = readConfigFromJdbcUrl(jdbcUrl);

host = dbConfigMap.get("host");

port = dbConfigMap.get("port");

db = dbConfigMap.get("db");

if (dbConfigMap.containsKey("user")) {

user = dbConfigMap.get("user");

} else {

user = username;

}

if (dbConfigMap.containsKey("password")) {

pwd = dbConfigMap.get("password");

} else {

pwd = password;

}

String shellpath = configBackupFolder + "/mysql-restore.sh";

File shellFile = new File(shellpath);

if (!FileUtil.exist(shellFile)) {

InputStream inputStream = new ClassPathResource("mysql/mysql-restore.sh").getInputStream();

FileOutputStream outputStream = new FileOutputStream(shellFile);

IoUtil.copy(inputStream, outputStream);

inputStream.close();

outputStream.close();

}

String mysqlpath = configBackupFolder + "/mysql";

if (!FileUtil.exist(mysqlpath)) {

InputStream inputStream = new ClassPathResource("mysql/mysql").getInputStream();

FileOutputStream outputStream = new FileOutputStream(new File(mysqlpath));

IoUtil.copy(inputStream, outputStream);

inputStream.close();

outputStream.close();

}

if (!shellFile.canExecute()) {

shellFile.setExecutable(true);

}

String shellCmd = String.format("%s -h %s -p %s -u %s -P %s -d %s -f %s", shellFile.getAbsolutePath(), host, port, user, pwd, db, backupFileAbPath);

log.info("Restore: {}", shellCmd);

Runtime.getRuntime().exec(shellCmd).waitFor();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

restoring = false;

}

}

// jdbc:mysql://localhost:3306/worksql?useUnicode=true&characterEncoding=UTF-8

private Map readConfigFromJdbcUrl(String jdbcUrl) {

Map configMap = new HashMap<>(8);

if (StrUtil.isEmpty(jdbcUrl)) {

return configMap;

}

Pattern pattern = Pattern.compile("jdbc:mysql:\\/\\/(.*)\\/(.*)", Pattern.CASE_INSENSITIVE);

Matcher matcher = pattern.matcher(jdbcUrl);

if (matcher.find()) {

// 主机地址和端口

String hostPort = matcher.group(1);

if (hostPort.contains(":")) {

String[] hostPorts = hostPort.split(":");

configMap.put("host", hostPorts[0]);

configMap.put("port", hostPorts[1]);

} else {

configMap.put("host", hostPort);

configMap.put("port", "3306");

}

// 连接的数据库

String dbParams = matcher.group(2);

if (dbParams.contains("?")) {

configMap.put("db", dbParams.split("\\?")[0]);

} else {

configMap.put("db",dbParams);

return configMap;

}

// 用户名和密码

String params = dbParams.split("\\?")[1];

if (params.length() > 1) {

params = params.substring(1);

String[] queries = params.split("&");

for (String query : queries) {

String[] keyValue = query.split("=");

if (keyValue[0].equals("user")) {

configMap.put("user", keyValue[1]);

continue;

}

if (keyValue[0].equals("password")) {

configMap.put("password", keyValue[1]);

}

}

}

return configMap;

}

return configMap;

}

}

这个本身是运行爱Springboot框架下的, 额外引入的包是hutool库和lombok

程序为了减少配置, 从springboot的配置文件中获取系统数据库的配置, 并解析jdbc的url获取相关连接参数

两个执行脚本和两个基本命令是放在resource目录下的mysql目录下, 跟其他代码一起打包到jar文件中发布

在执行的时候会先将打包在jar里的文件进行释放, 然后再执行, 在单机的并发处理上使用volatile关键词修饰的两个boolean变量防止出现两个进程备份进程同时进行(分布式环境下的问题处理有分布式下的处理方式)

mysql backup restore_mysql-backup-restore相关推荐

  1. mysql backup restore_MySQL Backup  Restore strategy

    1 Cold Backup & Restore 1.1 Copy Data file Shutdown mysql or "flush tables with read lock;& ...

  2. android:allowBackup=true 数据备份(adb backup)+查看(abe unpack backup.ab backup.tar)+恢复(adb re)

    备份 adb backup -nosystem -apk com.xiaomi.smarthome 查看 abe unpack backup.ab backup.tar 恢复 adb restore

  3. oracle end backup,oracle-backup-hot backup

    hot backup --********************************** -- 1.环境确认 归档模式.路径.文件 --***************************** ...

  4. SQL2005中使用backup、restore来备份和恢复数据库

    在SQL2005数据库中利用SQL语句进行数据备份与还原: 备份backup:backup database 数据库名称 tO disk = 备份路径 例:BACKUP DATABASE test T ...

  5. ERROR 1045 (28000): Access denied for user 'backup'@'mysql' (using password: YES)

    mysql远程连接时提示失败 [root@mysql ~]# /usr/local/mysql/bin/mysql -ubackup -p123456 -h 192.168.6.36 Warning: ...

  6. mysql backup文件_mysqlbackup使用

    安装mysqlbackup (从各种渠道获得mysqlbackup后) mysqlbackup是二进制文件,可直接使用 首先将mysqlbackup加入到系统环境中, cd /usr/bin ln - ...

  7. 手把手教你刷新BACKUP Bios

    有人说C.A69G-HDMI的BACKUP BIOS芯片不是真正意义上的ROM芯片,不能刷新.而C.A69G-K的BACKUP BIOS芯片是ROM芯片,但他却不知道怎么刷新. 经过偶试验,C.A69 ...

  8. SAP HANA Backup and Recovery

    2019独角兽企业重金招聘Python工程师标准>>> SAP HANA Backup and Recovery Skip to end of metadata Created by ...

  9. aws rds监控慢sql_使用AWS Backup备份AWS RDS SQL Server数据库

    aws rds监控慢sql This article gives you an overview of creating backups of AWS RDS SQL Server database ...

  10. 一键备份脚本 backup.sh

    做网站最重要的是什么?数据!数据,是网站之本,备份,是每一个站长都应该重视的事情.但同时,备份也是一件繁琐和重复的事情.所以,这些事情,肯定能做到自动化的. 下面来介绍一下这个一键备份脚本 backu ...

最新文章

  1. 腾讯AI Lab刷新人脸检测与识别两大测评国际记录,技术日调用超六亿
  2. Linux的常用的命令
  3. MySQL 语法问题:You can‘t specify target table ‘xxx‘ for update in FROM clause. 原因及解决方法
  4. ITFriend创业败局(四):菜鸟CEO的自我修养
  5. CentOS下为Web网站性能做测试
  6. 转:55个javascript处理网页技巧
  7. 状态压缩动态规划 -- 旅行商问题
  8. 【数据库原理】滨江学院姜青山 期末试卷知识点笔记整理 南京信息工程大学
  9. python day10
  10. Android--Handler的使用方法:在子线程中更新界面
  11. 日常管理随笔一 (转载)
  12. 浙大2016计算机考研分数线,2016浙江大学考研分数线公布
  13. ubuntu桌面管理环境 gdm3,KDM,lightdm
  14. 真牛,大神将Windows 10改造成Windows XP(EXPERIENCE Freestyle)
  15. 服务器系统崩溃真是惊险刺激,guanjunjc
  16. 010-WebBuilder-编写可复用模块+阶段实战效果与代码
  17. 希腊字母的发音(希腊人的发音)
  18. python pyecharts 画图 饼图柱状图
  19. 几张趣图带你了解程序员眼中的世界
  20. LabVIEW热敏电阻温度计程序

热门文章

  1. python批量打印mathcad_Print Conducto全能批量打印工具
  2. 大数据平台由哪些分析功能
  3. 大数据可视化有哪些作用和优点
  4. 大数据平台构建常见问题
  5. 计算机一级和答案,全国计算机一级操作题及答案
  6. AcWing 1801. 蹄子剪刀布(思维+暴力)
  7. oracle自动分区如何创建本地索引吗,创建与管理Oracle分区表和本地索引的实例解析...
  8. java判断五位数回文数_【视频+图文】Java经典基础练习题(五):键盘输入一个五位数,判断这个数是否为回文数...
  9. 基于Scikit-Learn进行命名实体识别和分类
  10. linux 如何从源码包安装软件?