目标
为了实现定时数据库备份。每天零晨启动,备份指定环境mysql数据库到另一机器上,并最多保存一周数据就好。
分析
1、mysql数据库备份,数据库自己提供了工具mysqldump
示例如下:
mysqldump -h $dbhost -u $username -p$password $database_name > $backup_dir/$database_name-$dd.sql
2、定时任务实现,最直接就是一个shell脚本实现,但是corntab没用明白,这里只好Java实现,主要用Timer实现
对Timer定时任务我们另说,路径如下:
工具
JDK1.8
intellij idea
mysql 8.0.15
实现
1、
bash.sh
这个脚本也是网上找的,只修改了JAVA_HOME和APP_HOME,分别是我本地JDK安装路径、工程jar路径
https://www.cnblogs.com/wangfajun/p/9585530.html
#!/bin/bash
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home
export JRE_HOME=/$JAVA_HOME/jre
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
#这里可替换为你自己的执行程序,其他代码无需更改
APP_NAME=/Users/****/Desktop/temp/dbtemp.jar
#使用说明,用来提示输入参数
usage() {
echo "Usage: sh robotcenter.sh [start|stop|restart|status]"
exit 1
}
#检查程序是否在运行
is_exist(){
pid=`ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}'`
#如果不存在返回1,存在返回0
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}
#启动方法
start(){
is_exist
if [ $? -eq 0 ]; then
echo "${APP_NAME} is already running. pid=${pid}"
else
nohup java -jar ${APP_NAME} >robotcenter.out 2>&1 &
fi
}
#停止方法
stop(){
is_exist
if [ $? -eq "0" ]; then
kill -9 $pid
else
echo "${APP_NAME} is not running"
fi
}
#输出运行状态
status(){
is_exist
if [ $? -eq "0" ]; then
echo "${APP_NAME} is running. Pid is ${pid}"
else
echo "${APP_NAME} is NOT running."
fi
}
#重启
restart(){
stop
sleep 5
start
}
#根据输入参数,选择执行对应方法,不输入则执行使用说明
case "$1" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac
2、
DBController.class
代码中,数据库导出和文件删除两个方法也是网上找的
import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author ydx
* @date 2019-02-22 15:38
*/
public class DBController {
private static String basePath = System.getProperty("user.dir") + File.separator + "dbBackFile";
//localhost altc1-appinfra-app-registry-1.vm.elenet.me
private static String dbHost = "localhost";
//root
private static String dbUser = "root";
//root
private static String dbPwd = "ydx";
//local_data qa_data
private static String dbName = "local_data";
private static String dateFormat = "yyyy-MM-dd-HH-mm-ss";
// 最多保留文件数
private static int saveFileMax = 2;
//文件格式
private static String fileFormat = "_" + dbName + ".sql";
public static void main(String[] args) throws Exception {
//定时执行,时间过了,立即执行
System.out.println("### job time:" + getTime());
System.out.println(Thread.currentThread().getName() + new Date().toString() + " run ");
new Timer("lun xun timer - ").schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + new Date().toString() + " run ");
String backName = new SimpleDateFormat(dateFormat).format(new Date()) + fileFormat;
try {
exportDatabaseTool(dbHost, dbUser, dbPwd, basePath, backName, dbName);
} catch (InterruptedException e) {
e.printStackTrace();
}
deleteFurthestFile(basePath, dateFormat, fileFormat, saveFileMax);
}
}, getTime(), 24 * 60 * 60 * 60 * 1000);//轮询 天 时 分 秒 毫秒
}
public static Date getTime() {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 2);
calendar.set(Calendar.MINUTE, 30);
calendar.set(Calendar.SECOND, 00);
Date time = calendar.getTime();
return time;
}
/**
* Java代码实现MySQL数据库导出
*
* @param hostIP MySQL数据库所在服务器地址IP
* @param userName 进入数据库所需要的用户名
* @param password 进入数据库所需要的密码
* @param savePath 数据库导出文件保存路径
* @param fileName 数据库导出文件文件名
* @param databaseName 要导出的数据库名
* @return 返回true表示导出成功,否则返回false。
* @author GaoHuanjie
*/
public static boolean exportDatabaseTool(String hostIP, String userName, String password, String savePath, String fileName, String databaseName) throws InterruptedException {
File saveFile = new File(savePath);
if (!saveFile.exists()) {// 如果目录不存在
saveFile.mkdirs();// 创建文件夹
}
if (!savePath.endsWith(File.separator)) {
savePath = savePath + File.separator;
}
PrintWriter printWriter = null;
BufferedReader bufferedReader = null;
try {
printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(savePath + fileName), "utf8"));
Process process = Runtime.getRuntime().exec(" mysqldump --column-statistics=0 -h" + hostIP + " -u" + userName + " -p" + password + " --set-charset=UTF8 " + databaseName);
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream(), "utf8");
bufferedReader = new BufferedReader(inputStreamReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
printWriter.println(line);
}
printWriter.flush();
if (process.waitFor() == 0) {//0 表示线程正常终止。
return true;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (printWriter != null) {
printWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 删除文件夹中最早产生的一个文件,如csv文件夹永远只保留max=30个最新的文件,超过30则删除一个文件
* 注意:是根据文件命名来delete的,所以必须要有公共的命名规则
*
* @param commonFileSuffix 文件夹下的文件名的公共后缀:如_log.txt
* @param fileDir 文件夹名称
* @param max 文件夹下最多的文件数量
* @param format 时间格式
*/
public static void deleteFurthestFile(String fileDir, String format, final String commonFileSuffix, final int max) {
try {
File dir = new File(fileDir);
if (!dir.exists()) {
return;
}
int currentNum = 0;
if (dir.exists()) {
File[] youxiaoFile = dir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(fileFormat);
}
});
currentNum = youxiaoFile.length; //得到当前文件夹文件数目
}
if (currentNum > max) {
do {
//得到文件名的一个map
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
Map<Long, String> map = new HashMap();
for (File file1 : dir.listFiles()) {
if (file1.getName().startsWith(".")) {
continue;
}
String time = file1.getName().replace(commonFileSuffix, "");
try {
Date date = simpleDateFormat.parse(time);
map.put(date.getTime(), file1.getName()); //<1233282716409,2018-2-1_log.txt>
} catch (RuntimeException e) {
System.out.println(" file:" + file1.getName());
}
}
//产生时间最早的文件名
long lt = Long.valueOf(Collections.min(map.keySet()).toString());
File f1 = new File(fileDir + File.separator + map.get(lt));
if (f1.exists()) {
System.out.println(" delete file:" + f1.getName());
f1.delete();
currentNum--;
}
} while (currentNum > max);
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
总结
1、文件删除代码中,do while是后加的,原因是先前的代码只会删除最晚的一个文件。与这里的需求不合,这边只要最近一周时间的数据,其它的都可以删除,所以这里加了个循环
2、文件删除代码中,在获取当前文件下有多少个文件的实现,这里对文件名做了判断,原因是我这边用Mac开发的,文件夹里会有隐藏文件,为了不影响日期格式解析,这里做了过滤
3、intellij打包时,习惯性用maven打包,但是运行jar时,会找不会入口,原因是MANIFEST.MF中没有Main-Class配置,改用intellij自己的build打包就好。