用Shell实现备份Mysql数据

需求

Mysql版本:5.7.38 Linux系统:7.0

服务器A:192.168.202.129 服务器B:192.168.202.130

初始化

192.168.202.129

全量:

根目录:/home/backup/all

备份数据保存位置:/home/backup/all/data

日志保存位置:/home/backup/all/bak.log

增量:

根目录:/home/backup/incr

备份数据保存位置:/home/backup/incr/data

日志保存位置:/home/backup/incr/bak.log

下次开始备份的序列号:/home/backup/incr/sn

临时保存备份的增量binlog日志:/home/backup/incr/cp_binglog

192.168.202.130

全量:

备份数据目录:/home/backup/all/{date}

日期home/backup/all 目录,增量也是。

增量:

备份数据目录:/home/backup/incr/{date}

全量

  • 每七天进行一次全量备份,并刷新binlog日志,保存下一次增量开始备份的序列。
  • 打包sql文件为tar包,使用md5sum生成tar包的效验值,将效验文件与tar包一起打包压缩。
  • 使用scp将129服务器上压缩的tar.gz包拷贝到130服务器。
  • ssh连接130服务器,解压压缩包,使用md5sum效验文件完整性。

增量

  • 若当天存在全量备份,则直接退出。
  • 首先生成新的binlog日志,然后获取此次需要同步的binlog序列号,获取mysql目录下的binlog日志,先拷贝到本地的临时目录。将所有增量的binlog日志打成tar包,使用md5sum生成效验文件,并一起打包压缩。
  • 使用scp将129服务器上压缩的tar.gz包拷贝到130服务器。
  • ssh连接130服务器,解压压缩包,使用md5sum效验文件完整性。

清理过期文件

​ 清理超过指定时间的备份文件

定时

每天执行脚本

1
2
3
4
5
6
7
8
9
10
# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

方式一:

进入/etc/cron.d目录,创建mysqldump.sh文件,赋予644权限。

/var/log/cron 可查看定时器执行日志。

1
2
#每天凌晨3点执行 XXX 替换成数据库备份脚本,绝对路径
0 0 3 1 * ? root sh XXX.sh

方式二:

直接修改 /etc/crontab,内容一致

准备

安装expect用于ssh交互

1
yum -y install expect

scp免密登录

1
2
#129上执行,将生成的公钥复制到 130的 /root/.ssh/authorized_keys
ssh-keygen -t rsa

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#/bin/bash
#Description: mysql binlog备份
#Author: chris
#Created Time: 2023/11/22 21:45

db_user='root'
db_pwd='123456'
#备份目录
base_dir="/home/backup"
#全量 7天一次全量
all_dir="${base_dir}/all"
#增量 1天一次增量
incr_dir="${base_dir}/incr"
date=`date +%Y%m%d`
copy_ip_addr=192.168.202.130
interval_day=7
expired_day=32

incr_log=$incr_dir/bak.log
all_log=$all_dir/bak.log

#存放备份数据的目录
incr_data=$incr_dir/data
all_data=$all_dir/data


#保存最后一个复制的binglog序列号
last_binlog_sn=$incr_dir/sn
#临时存放binlog文件的目录
cp_binglog_dir=$incr_dir/cp_binglog

#
init(){

if [ ! -d ${incr_data} ]
then
mkdir -p $incr_data
fi

if [ ! -d ${all_data} ]
then
mkdir -p $all_data
fi


if [ ! -d ${all_dir} ]
then
mkdir -p $all_dir
fi

if [ ! -d ${incr_dir} ]
then
mkdir -p $incr_dir
fi

if [ ! -d ${cp_binglog_dir} ]
then
mkdir -p $cp_binglog_dir
fi

if [ ! -f ${incr_log} ]
then
touch ${incr_log}
fi

if [ ! -f ${all_log} ]
then
touch $all_log
fi

if [ ! -f ${last_binlog_sn} ];then
touch ${last_binlog_sn}
chmod 740 ${last_binlog_sn}
fi

}

log(){
echo "[`date '+%Y-%m-%d %H:%M:%S'`]:$1"
}

last_dump_file=`find ${all_data}/ -type f -mtime -${interval_day} |wc -l`

allBackup(){
# 通过文件名拿到最后一次备份的时间
# last_date=ls ${all_dir} | tail -1 | awk -F "_" {'print $2'} | awk -F "." {'print $1'}

#不为空
if [ ${last_dump_file} -gt 0 ]
then
log "mysqldump has done ${last_dump_file}">> $all_log
return
fi

log "begin mysqldump -> ${all_data}/all_${date}.sql" >> $all_log

#全量
#-A备份所有数据库 或 --all-databases
#--quick 不缓存,直接导出
#--flush-logs dump时生成新的binlog日志
#--master-data=2 配合--flush-logs使用,只生成一次日志,不然每个数据库都会生成一次日志。 dump出的文件中,保存 binlog 和 pos点,并注释生成的这行信息。增量备份需要使用
# --master-data=1 dump出的文件中,不注释这行信息,恢复时会执行
#--single-transaction 保证导出开始和结束的数据一致性,仅InnoDB。与--lock-tables选项互斥,相当于开启可重复读,在事务开始、事务结束的时候,多次select查询的值都是一样的。
#--default-character-set 设置字符集为 utf8mb4,根据数据库原始的字符集确定,SHOW VARIABLES LIKE 'character_set%'查询支持的字符集
cd ${all_data}
mysqldump -u${db_user} -p${db_pwd} -A --quick --flush-logs --master-data=2 --single-transaction --default-character-set=utf8mb4 > all_${date}.sql

if [ $? -eq 0 ];then
log "mysqldump ${all_data}/all_${date}.sql success" >>$all_log
else
log "mysqldump ${all_data}/all_${date}.sql error" >>$all_log
exit 1
fi

if [ ! -d ${date} ];then
mkdir -p ${date}
fi
next_sn=`grep "MASTER_LOG_FILE='*'" all_${date}.sql | awk -v flag="'" -F "." '{split($2,arr,flag)} END {print arr[1]}'`
echo $next_sn > ${last_binlog_sn}
log "mysqldump then save max binglog_sn and sn= ${next_sn}" >> $all_log

tar -cf ${date}/all_backup_${date}.tar all_${date}.sql
rm -f all_${date}.sql
cd ${date}
# cd 到目录里面在md5sum
md5sum all_backup_${date}.tar > all_md5sum.txt
cd ${all_data}
tar -zcf all_backup_${date}.tar.gz ${date}
scp all_backup_${date}.tar.gz @${copy_ip_addr}:$all_dir

if [ $? -eq 0 ];then
log "scp ${all_data}/all_backup_${date}.tar.gz success and rm" >>$all_log
rm -fr ${all_data}/all_backup_${date}.tar.gz
else
log "scp ${all_data}/all_backup_${date}.tar.gz error" >>$all_log
exit 1
fi

#yum -y install expect 安裝
/usr/bin/expect<<EOF
spawn ssh root@${copy_ip_addr}
expect {
"*Last login"
{send "cd $all_dir\r"}
}
expect "*]#" {
send "tar -zxf all_backup_${date}.tar.gz\r
rm -f all_backup_${date}.tar.gz\r
cd ${date}\r
md5sum -c all_md5sum.txt\r"
}
expect eof
EOF

if [ $? -eq 0 ]
then
log "md5sum check ${copy_ip_addr} $all_dir/all_backup_${date}.tar.gz success" >>$all_log
rm -rf ${incr_data}/*
else
log "md5sum check ${copy_ip_addr} $all_dir/all_backup_${date}.tar.gz fail" >>$all_log
exit 1
fi
}

binlog_path="/var/lib/mysql"
last_dump_file=`find ${all_data}/ -type f -mtime -${interval_day} | wc -l`
#last_dump_file=`find ${all_data}/ -type d | wc -l`
incrBackup(){

#没有全量备份,则进行增量
if [ $last_dump_file -gt 0 ]
then
log "has mysqldump $last_dump_file and return" >> ${incr_log}
return 0
fi
#刷新日志
mysql -u$db_user -p$db_pwd -e "flush logs"
last_sn=`cat ${last_binlog_sn}`

if [ -z $last_sn ];then
$last_sn=000
fi

count=`cat ${binlog_path}/mysql-bin.index |wc -l`
index=0
#跟java的不一样,有多少个数据就循环多少次
for content in `cat ${binlog_path}/mysql-bin.index`
do
index=$((index+1))
sn=`echo $content | awk -F "." '{print $2}'`

if [ $last_sn ] && [ $sn -ge $last_sn ] || [ -z $last_sn ];then
# 跳过最新的
if [ $index -lt $count ];then
cp $content $cp_binglog_dir
log "$content -> $cp_binglog_dir cp done" >> ${incr_log}
fi
fi

if [ $index -ge $count ];then
echo $sn > ${last_binlog_sn}
log "save max binglog_sn and sn= ${sn}" >> ${incr_log}
fi
done

file_count=`ls $cp_binglog_dir |wc -l`
if [ $file_count -gt 0 ];then
cd ${incr_data}
#创建压缩目录
if [ ! -d ${date} ];then
mkdir -p ${date}
fi
# 建议使用相对路径打包,绝对路径打包,解压出来的包也会带上路径,并且少了开始的/
# cp_binglog 保存复制binlog的文件
cd $incr_dir
tar -cf incr_backup_${date}.tar cp_binglog/*
mv -f incr_backup_${date}.tar ${incr_data}/${date}/
rm -fr $cp_binglog_dir/*
cd ${incr_data}/${date}
md5sum incr_backup_${date}.tar >incr_md5sum.txt
cd ${incr_data}
tar -zcf incr_backup_${date}.tar.gz ${date}
scp incr_backup_${date}.tar.gz @${copy_ip_addr}:$incr_dir
if [ $? -eq 0 ];then
log "${incr_data}/incr_backup_${date}.tar.gz scp success and rm" >> ${incr_log}
rm -f ${incr_data}/incr_backup_${date}.tar.gz
else
log "${incr_data}/incr_backup_${date}.tar.gz scp error" >> ${incr_log}
fi

/usr/bin/expect <<incrEOF
spawn ssh root@${copy_ip_addr}
expect {
"*Last login"
{send "cd $incr_dir\r"}
}
expect "*]#" {
send "tar -zxf incr_backup_${date}.tar.gz\r
rm -f incr_backup_${date}.tar.gz\r
cd ${date}\r
md5sum -c incr_md5sum.txt\r"
}
expect eof
incrEOF

if [ $? -eq 0 ]
then
log "md5sum check ${copy_ip_addr} $incr_dir/incr_backup_${date}.tar.gz success" >>${incr_log}
else
log "md5sum check ${copy_ip_addr} $incr_dir/incr_backup_${date}.tar.gz error" >>${incr_log}
fi
else
log "no incr log to cp" >>${incr_log}
fi

# 远程copy 记得配置免密登录
# ssh-keygen -t rsa 将生成的公钥复制到 130 /root/.ssh/authorized_keys

}

clearExpiredFile(){

log "delete $all_data expired file: ">> {all_log}
echo `find $all_data/* -type f -mtime +$expired_day -exec ls \;` >> {all_log}
find $all_data/* -type f -mtime +$expired_day -exec rm -rf {} \;


log "delete $incr_data expired file: ">> {incr_log}

echo `find $incr_data/* -type f -mtime +$expired_day -exec ls \;` >>{incr_log}
find $incr_data/* -type f -mtime +$expired_day -exec rm -rf {} \;

}
#初始化
init
#全量
allBackup
#增量
incrBackup
#清楚过期文件
clearExpiredFile
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信