这是我最近维护的线上数据库的备份脚本,断断续续code了不少。
就目前来看效果很不错,当然主要目的是抛(rang)砖(wo)引(zhuang)玉(bi)。
backup.sh
点击下载
backup.sh 会调用对应的配置文件mysql-cluster-list,public.conf,backuptime文件,调度多个备份脚本执行,并统计结果
#!/bin/bash #Author weskiller 2016-04-08 #Email:[email protected] #定义环境变量 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin export LANG="en_US.UTF-8" #定义路径 basedir=/root/scripts/backup log=$basedir/backup.log date=`date +%F` starttime_conf=$basedir/backuptime mysql_cluster_conf=$basedir/mysql-cluster-list #superadmin邮件地址,当脚本文件缺失的时候,会发送邮件 Semail='[email protected]' #下标变量 declare -i sub=0 #设置循环检查的间隔时间 check_sleep=60 #设置备份超时时间(每天下午6点,用户高峰前期) deadtime=`date --date "18:00" +%s` #获取mysql-cluster的列表 mysql_cluster_list=(192.168.1.1) #或者通过远程的web页面获取列表 #cluster_curl=http://192.168.1.1/mysql_cluster.txt #for list in `curl "$cluster_curl"`;do # mysql_cluster_list[$sub]=$list; #done #标记日志备份开始 echo "" >> $log echo "$date backup.sh start" >> $log #加载公有配置 if [ -f "$basedir/config/public.conf" ];then . $basedir/config/public.conf else /bin/mail -r "backup.sh" -s "backup.sh scripts is interrupt" "$Semail" <<< "`date +%F\ %T` - backup scripts is not performing! the public.conf is miss." echo "`date +%T` - backup scripts is not performing! the public.conf is miss." >> $log exit 1 fi #检查开始备份时间配置文件 if [ ! -f "$starttime_conf" ];then sendemail "backup.sh scripts is interrupt" "`date +%F\ %T` - backup scripts is not performing! can't find configuration with ${starttime_conf}." echo "`date +%T` - backup scripts is not performing! can't find configuration with ${starttime_conf}." >> $log exit 1 fi #加载配置文件mysql_cluster_conf,如果存在的话 if [ -f "$mysql_cluster_conf" ];then mysql_cluster_list=(`cat "$mysql_cluster_conf"`) fi #检测后端mysql_cluster declare -i change=0 for i in `seq 0 $((${#mysql_cluster_list[@]}-1))`;do nc -z ${mysql_cluster_list[$i]} 3306 >/dev/null 2>&1 if [ $? -eq 0 ];then continue else echo "detected ${mysql_cluster_list[$i]} is down. will be remove from mysql cluster." >> $log unset mysql_cluster_list[$i] change=1 fi done #重新定义有效的mysql_cluster_list if [ $change -eq 1 ];then mysql_cluster_list=(${mysql_cluster_list[@]}) fi #极端情况下后端集群全部宕机 if [ ${#mysql_cluster_list[@]} -eq 0 ];then sendemail "backup.sh scripts is interrupt" "`date +%F\ %T` - backup scripts is not performing! without living mysql cluster." echo "`date +%T` - backup scripts is not performing! without living mysql cluster." >> $log exit 1 fi #手动备份 if [ "x$1" == "x" ];then manual=0 elif [ "$1" == "manual" ];then manual=1 echo "backup.sh is start with manual." >> $log fi #重定向所有标准错误信息 #exec 2>$basedir/backup.error #备份循环 sub=0 for project in $basedir/config/*-*;do script=`basename $project` project_name=${script%-*} #默认模式下,根据设定的时间备份 if [ "$manual" -eq 0 ];then #获取配置时间 project_start_time="`awk 'BEGIN{FS="="}/'$script'/{print $2}' $starttime_conf`" if [ $? -eq 0 ];then project_timestamp=`date --date "$project_start_time" +%s` if [ "$project_timestamp" -le "`date +%s`" ];then echo "`date +%F\ %T` - $script backuptime is error" >> $log continue fi else echo "`date +%F\ %T` - $script backuptime is not find " >> $log continue fi waittime="$((project_timestamp-`date +%s`))" #手动备份,根据设定的时间延迟 elif [ "$manual" -eq 1 ];then first=`awk 'BEGIN{FS="="}{print $2}' backuptime|grep -P "^[^ ]" |sort -n|head -1` lag=$((`date --date '1 minute' +%s`-`date --date "$first" +%s`)) project_start_time="`awk 'BEGIN{FS="="}/'$script'/{print $2}' $starttime_conf`" if [ $? -eq 0 ];then project_timestamp=$((`date --date "$project_start_time" +%s`+$lag)) else echo "`date +%F\ %T` - $script backuptime is not find " >> $log continue fi waittime="$((project_timestamp-`date +%s`))" fi host=${mysql_cluster_list[$((sub%${#mysql_cluster_list[@]}))]} #使用screen,定时放入后台 screen -S $project_name -d -m /bin/bash "$project" "$host" "$waittime" #放入项目数组 project_list[$sub]="$script" ((sub++)) done #监控今天的备份日志,当所有备份完成后发送邮件通知 line=`grep -Pn "$date backup.sh start" $log|sed -n '$p'|awk 'BEGIN{FS=":"}{print $1}'` while sleep $check_sleep && [ ${#project_list[@]} -gt 0 ];do sub=0 for i in ${project_list[@]};do if sed -n "$line,\$p" $log |grep -Pq "$i backup (success|failed)" ;then unset project_list[$sub] fi ((sub++)) done project_list=(${project_list[@]}) if [ "`date +%s`" -ge "$deadtime" ];then sendemail "backup is overtime" "`date +%F\ %T` -- project:${project_list[*]} already overtime" fi done #判断是否有失败的项目 if sed -n "$line,\$p" $log |grep -Pq "backup failed";then backup_failed=1 else backup_failed=0 fi #标记日志备份结束 echo "$date backup.sh finish" >> $log #统计失败项目邮件 if [ $backup_failed -eq 1 ];then sendemail "some project backup failed" "`date +%F\ %T` -- backup failed project:`sed -n $line,'$p' $log|grep -Po "\S*(?= backup failed)"|tr '\n' ' '`" fi #生成WEB网页 sed -n -e "$line,\$p" -e '1a<pre>' -e '$a</pre>' $log > /backup/backup.html #日志整理 if [ `date --date '1 day' +%d` -eq 1 ];then . $basedir/logrotate.sh fi #优雅的退出 exit 0
scripts
点击下载
scripts 是真正的备份执行脚本,加载public.conf获取公共配置信息,针对不同的线上业务和数据库可以自定义操作,或者根据需求修改,这里只是提供模板。
#!/bin/bash #author weskiller 2016-04-08 #description:project backup scripts unit #2015-05-16:新增隔天自动备份功能 #定义环境变量 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin export LANG="en_US.UTF-8" #获取脚本包含的信息 dir=/root/scripts/backup scripts=`basename $0` backup_host=192.168.1.${scripts#*-} game_label=${scripts%-*} #定义各种需要的时间戳 month=`date +%m` last_month=`date --date "1 months ago" +%m` day=`date +%d` yesterday=`date --date "1 day ago" +%d` #定义数据库信息 game_database="p${game_label}_logs1" game_backup_database="p${game_label}_logs_backup1" today="$(date --date @$(($(date --date "`date +%F`" +%s)-3600*8)) +%F\ %T)" delete_gamelog_time="$(date --date @$((`date --date "$today" +%s`+7*3600)) +%F\ %T)" three_months="`date --date '4 month ago' +%m`" #加载公共配置 if [ -f "$dir/config/public.conf" ];then . $dir/config/public.conf else echo "`date +%F\ %T`:not find public.conf" > $dir/config/$scripts.error exit 1; fi #获取backup.sh传递的参数 if [ "x$1" == "x" ];then echo "`date +%F\ %T`:not received parameter:mysql backup host " >> $dir/log/$scripts.log exit 1 else db_host="$1" fi if [ "x$2" == "x$2" ];then sleep $2 if [ $? -ne 0 ];then echo "`date +%F\ %T`:received sleep time parameter,but sleep error " >> $dir/log/$scripts.log exit 1 fi else echo "`date +%F\ %T`:not received parameter:sleep time(s) " >> $dir/log/$scripts.log exit 1 fi #开始时间戳 start_time="`date +%s`" #创建文件夹,如果不存在的话 /usr/bin/ssh root@$backup_host "mkdir -p $backupdir/$game_label/months/$month" #创建xtrabakup命令 if [ "$day" -eq 1 ];then innobackup_command="/usr/bin/innobackupex --no-timestamp $backupdir/$game_label/months/$month/$day" /usr/bin/ssh root@$backup_host "rm -f $backupdir/$game_label/days" /usr/bin/ssh root@$backup_host "ln -sf $backupdir/$game_label/months/$month $backupdir/$game_label/days" #删除三个月之前的备份 delete_three_backup="rm -rf $backupdir/$game_label/months/$three_months" else #获取days下的目录,确定日期 uday=$(ssh root@$backup_host "basename \`ls -d $backupdir/$game_label/days/*|grep -Pv "tar.bz2$"\` 2>/dev/null") if [ ${uday:-0} -eq $yesterday ];then innobackup_command="/usr/bin/innobackupex --incremental --incremental-basedir $backupdir/$game_label/days/$yesterday --no-timestamp $backupdir/$game_label/days/$day ; result=\$? ; if [ \$result -ne 0 -a ! -e $backupdir/$game_label/days/$day/xtrabackup_info ];then rm -rf $backupdir/$game_label/days/$day;fi ;exit \$result" else innobackup_command="/usr/bin/innobackupex --incremental --incremental-basedir $backupdir/$game_label/days/$uday --no-timestamp $backupdir/$game_label/days/$day ; result=\$? ; if [ \$result -ne 0 -a ! -e $backupdir/$game_label/days/$day/xtrabackup_info ];then rm -rf $backupdir/$game_label/days/$day;fi ;exit \$result" jump=1 fi fi #创建mysqldump命令 mysqldump_command="mysqldump --single-transaction --skip-add-locks --skip-lock-tables --no-create-info --replace --where \"create_time < '$today'\" $game_database logs |mysql -u$db_user -h$db_host -p$db_pass $game_backup_database" #创建清理日志命令 mysql_command="mysql -e \"delete from $game_database.logs where create_time < '$delete_gamelog_time';\"" #压缩前一天的备份文件 if [ "$day" -eq 1 ];then tar_command="tar -jcf $backupdir/$game_label/months/$last_month/$yesterday.tar.bz2 -C $backupdir/$game_label/months/$last_month $yesterday --remove-files" else if [ ${uday:-0} -eq $yesterday ];then tar_command="tar -jcf $backupdir/$game_label/days/$yesterday.tar.bz2 -C $backupdir/$game_label/days $yesterday --remove-files" else tar_command="tar -jcf $backupdir/$game_label/days/${uday}.tar.bz2 -C $backupdir/$game_label/days $uday --remove-files" fi fi #日志标记开始 echo "" >> $dir/log/$scripts.log echo "`date +%F\ %T` $scripts start" >> $dir/log/$scripts.log #在远程备份客服端上执行命令 for command in "$innobackup_command" "$mysqldump_command" "$mysql_command" "$tar_command" "$delete_three_backup" ;do #先判断变量内容,确保ssh命令正常执行 if [ -z "$command " ] || echo "$command"|grep -Pq "^\s*$" ;then continue; fi ssh root@$backup_host "$command" 2>$dir/log/$scripts.error 1>/dev/null if [ $? -eq 0 ];then echo "`date +%F\ %T` -- \"$command\" performing success." >> $dir/log/$scripts.log else echo "`date +%F\ %T` -- \"$command\" performing failed." >> $dir/log/$scripts.log # sendemail "$scripts" "`cat $dir/log/$scripts.error`" log "failed" "$scripts" exit 1 fi done #删除错误的输出 rm -rf $dir/log/$scripts.error #标记日志结束 echo "`date +%F` $scripts finish" >> $dir/log/$scripts.log #输出backup.log日志 if [ ${jump:-0} -eq 1 ];then log "success" "$scripts" "$((`date +%s`-start_time))" "$uday $day" else log "success" "$scripts" "$((`date +%s`-start_time))" fi #优雅的退出 exit 0
public.conf
点击下载
public.conf 公共配置文件,包含了日志输出和维护邮件组,mysql验证
#this is public config that all scripts need to be loaded #定位绝对路径 backupdir=/backup #备份验证 db_user= db_pass= #负责人E-Mail地址 adminmail=([email protected] [email protected]) sendemail () { for mail in ${adminmail[@]};do /bin/mail -r "backup.sh" -s "$1" "$mail" <<< "$2" done } #输出日志函数 log () { case $1 in success) if [ $3 -lt 60 ];then consume="${3}s" elif [ $3 -lt 3600 ];then consume="$(($3/60))m$(($3%60))s" else consume="$(($3/(60*60)))h$((($3%(60*60))/60))m$(($3%60))s" fi if [ "x$4" == "x" ];then echo "`date +%F\ %T` - $2 backup $1. used ${consume}." >> /root/scripts/backup/backup.log else echo "`date +%F\ %T` - $2 backup $1. used ${consume}. ${4% *} => ${4#* } " >> /root/scripts/backup/backup.log fi ;; failed) echo "`date +%F\ %T` - $2 backup $1" >> /root/scripts/backup/backup.log ;; esac }
backuptime
点击下载
backuptime 主要用于设定备份数据库的时间
#测试数据库 test-1=01:00 test-2=00:00 #线上数据库 produce-101=03:00 produce-102=03:30