【LINUX】服务器数据库自动备份脚本实现

【LINUX】服务器数据库自动备份脚本实现

一、前言

在服务器运维过程中,数据库备份是一项至关重要的工作。数据是企业的核心资产,一旦数据丢失或损坏,可能会造成不可估量的损失。因此,建立一个可靠的自动备份机制是保障数据安全的基础措施。本文将详细介绍如何在Linux服务器上创建一个自动备份数据库的脚本,并通过crontab设置定时任务,实现数据库的自动备份。

二、准备工作

(一)环境要求

在开始之前,请确保您的服务器环境满足以下条件:

  1. Linux操作系统(本文以Ubuntu/CentOS为例)
  2. 已安装数据库服务(MySQL/MariaDB/PostgreSQL等)
  3. 具有数据库管理员权限
  4. 足够的磁盘空间用于存储备份文件

(二)备份策略规划

在创建备份脚本前,笔者建议先规划好备份策略:

  1. 备份频率:每天、每周或自定义周期
  2. 备份时间:选择服务器负载较低的时间段
  3. 备份保留期:决定保留多少天的备份文件
  4. 备份存储位置:本地存储、远程服务器或云存储
  5. 备份方式:全量备份或增量备份

三、创建备份脚本

(一)MySQL/MariaDB备份脚本

以下是一个完整的MySQL/MariaDB数据库备份脚本示例:

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
#!/bin/bash

# 数据库配置信息
DB_USER="数据库用户名"
DB_PASS="数据库密码"
# 如果需要备份多个数据库,用空格分隔
DATABASES="数据库1 数据库2 数据库3"
# 也可以备份所有数据库
# DATABASES="--all-databases"

# 备份设置
BACKUP_DIR="/data/backups/mysql" # 备份文件存储路径
DATE=$(date +"%Y%m%d") # 当前日期,用于文件命名
TIME=$(date +"%H%M%S") # 当前时间,用于更精确的文件命名
BACKUP_RETAIN_DAYS=7 # 备份保留天数

# 是否启用自动清理(0表示禁用自动清理,1表示启用)
# 对于手动备份,可以设置为0;对于定时任务,可以设置为1
AUTO_CLEANUP=0

# 创建备份目录(如果不存在)
mkdir -p $BACKUP_DIR

# 记录开始时间
echo "备份开始时间:$(date)" >> "$BACKUP_DIR/backup_log.log"

# 备份每个数据库
for db in $DATABASES; do
echo "开始备份数据库: $db" >> "$BACKUP_DIR/backup_log.log"

# 创建带有日期和时间的备份文件名,避免覆盖同一天的备份
BACKUP_FILENAME="${db}_${DATE}_${TIME}.sql.gz"

# 创建备份文件
mysqldump --user=$DB_USER --password=$DB_PASS --host=localhost \
--single-transaction --quick --lock-tables=false \
--databases $db | gzip > "$BACKUP_DIR/$BACKUP_FILENAME"

# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "数据库 $db 备份成功: $BACKUP_FILENAME" >> "$BACKUP_DIR/backup_log.log"
else
echo "数据库 $db 备份失败" >> "$BACKUP_DIR/backup_log.log"
fi
done

# 只有在启用自动清理的情况下才删除过期备份
if [ "$AUTO_CLEANUP" -eq 1 ]; then
echo "执行清理操作,删除${BACKUP_RETAIN_DAYS}天前的备份文件..." >> "$BACKUP_DIR/backup_log.log"
find $BACKUP_DIR -name "*.sql.gz" -type f -mtime +$BACKUP_RETAIN_DAYS -exec rm -f {} \;
else
echo "自动清理已禁用,保留所有备份文件" >> "$BACKUP_DIR/backup_log.log"
fi

# 记录结束时间
echo "备份结束时间:$(date)" >> "$BACKUP_DIR/backup_log.log"
echo "-------------------------------------" >> "$BACKUP_DIR/backup_log.log"

# 可选:将备份文件同步到远程服务器
# rsync -avz $BACKUP_DIR user@remote_server:/path/to/backup/

mysqldump参数详解

脚本中使用的mysqldump命令参数说明:

  1. 基本连接参数:

    • --user=$DB_USER: 指定连接数据库的用户名
    • --password=$DB_PASS: 指定连接数据库的密码
    • --host=localhost: 指定数据库主机地址,localhost表示本地
  2. 性能与一致性相关参数:

    • --single-transaction: 在一个事务中执行所有表的转储,确保备份时数据的一致性。特别适用于InnoDB表,能在不锁表的情况下获得一致的备份。
    • --quick: 从服务器逐行检索表中的数据,而不是将整个结果集缓存在内存中。减少内存使用,适合大型数据库备份。
    • --lock-tables=false: 禁用LOCK TABLES,与–single-transaction一起使用时可以避免锁定表,减少对数据库操作的影响。
  3. 备份范围参数:

    • --databases $db: 指定要备份的数据库名称。这个参数会在备份文件中包含CREATE DATABASE和USE语句,还原时不需要指定数据库名

    其他可选的备份范围参数:

    • --all-databases: 备份所有数据库
    • --ignore-table=db_name.tbl_name: 排除指定的表
    • --tables: 仅备份指定的表

    如何创建可在任意数据库恢复的备份:
    如果想创建可在任意数据库恢复的备份(不限于原数据库名),可以使用以下参数组合:

    1
    2
    3
    4
    5
    # 创建可在任意数据库恢复的备份
    mysqldump --user=$DB_USER --password=$DB_PASS --host=localhost \
    --single-transaction --quick --lock-tables=false \
    --no-create-db \
    数据库名 | gzip > "备份文件.sql.gz"

    关键参数说明:

    • 不使用--databases参数:直接使用数据库名作为参数
    • 添加--no-create-db:不生成CREATE DATABASE语句

    恢复到任意数据库的方法:

    1
    2
    3
    4
    5
    # 先创建目标数据库(如果尚不存在)
    mysql -u 用户名 -p -e "CREATE DATABASE IF NOT EXISTS 目标数据库名"

    # 然后将备份恢复到该数据库
    gunzip < 备份文件.sql.gz | mysql -u 用户名 -p 目标数据库名
  4. 其他常用参数(脚本中未使用但可考虑添加):

    • --no-data: 只备份表结构,不备份数据
    • --no-create-info: 只备份数据,不备份表结构
    • --routines: 包含存储过程和函数
    • --triggers: 包含触发器
    • --events: 包含事件调度器
    • --set-gtid-purged=OFF: 不在备份中包含GTID信息,适用于主从复制环境
    • --skip-extended-insert: 使每个INSERT语句包含一条记录,便于编辑(但会增加备份文件大小)
    • --hex-blob: 使用十六进制表示法导出二进制列
    • --complete-insert: 使用包含列名的完整INSERT语句
    • --master-data=2: 添加CHANGE MASTER命令作为注释,用于主从复制
    • --where="条件": 只备份符合WHERE条件的记录
    • --opt: 启用多个优化选项,是默认开启的
    • --skip-opt: 禁用–opt启用的优化选项
    • --compress: 压缩客户端和服务器之间的通信

选择适当的mysqldump参数对于获得高质量的备份至关重要。根据数据库大小、业务需求和系统负载,可以调整这些参数以优化备份过程。

(二)PostgreSQL备份脚本

如果您使用的是PostgreSQL数据库,可以使用以下脚本:

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
#!/bin/bash

# 数据库配置信息
DB_USER="数据库用户名"
DB_PASS="数据库密码"
# 如果需要备份多个数据库,用空格分隔
DATABASES="数据库1 数据库2 数据库3"

# 备份设置
BACKUP_DIR="/data/backups/postgresql" # 备份文件存储路径
DATE=$(date +"%Y%m%d") # 当前日期,用于文件命名
TIME=$(date +"%H%M%S") # 当前时间,用于更精确的文件命名
BACKUP_RETAIN_DAYS=7 # 备份保留天数

# 是否启用自动清理(0表示禁用自动清理,1表示启用)
# 对于手动备份,可以设置为0;对于定时任务,可以设置为1
AUTO_CLEANUP=0

# 创建备份目录(如果不存在)
mkdir -p $BACKUP_DIR

# 记录开始时间
echo "备份开始时间:$(date)" >> "$BACKUP_DIR/backup_log.log"

# 备份每个数据库
for db in $DATABASES; do
echo "开始备份数据库: $db" >> "$BACKUP_DIR/backup_log.log"

# 创建带有日期和时间的备份文件名,避免覆盖同一天的备份
BACKUP_FILENAME="${db}_${DATE}_${TIME}.backup"

# 创建备份文件
PGPASSWORD="$DB_PASS" pg_dump -U $DB_USER -d $db -F c -b -v -f "$BACKUP_DIR/$BACKUP_FILENAME"

# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "数据库 $db 备份成功: $BACKUP_FILENAME" >> "$BACKUP_DIR/backup_log.log"
else
echo "数据库 $db 备份失败" >> "$BACKUP_DIR/backup_log.log"
fi
done

# 只有在启用自动清理的情况下才删除过期备份
if [ "$AUTO_CLEANUP" -eq 1 ]; then
echo "执行清理操作,删除${BACKUP_RETAIN_DAYS}天前的备份文件..." >> "$BACKUP_DIR/backup_log.log"
find $BACKUP_DIR -name "*.backup" -type f -mtime +$BACKUP_RETAIN_DAYS -exec rm -f {} \;
else
echo "自动清理已禁用,保留所有备份文件" >> "$BACKUP_DIR/backup_log.log"
fi

# 记录结束时间
echo "备份结束时间:$(date)" >> "$BACKUP_DIR/backup_log.log"
echo "-------------------------------------" >> "$BACKUP_DIR/backup_log.log"

(三)脚本权限设置

创建脚本后,需要为其添加执行权限:

1
chmod +x /path/to/backup_script.sh

四、设置定时任务

(一)使用crontab设置定时任务

通过crontab可以设置脚本的自动执行时间:

1
2
# 编辑当前用户的crontab
crontab -e

添加以下内容(以每天凌晨2点执行为例):

1
2
# 每天凌晨2点执行备份脚本,并启用自动清理功能
0 2 * * * /path/to/backup_script.sh 1

如果您使用了本文修改后的脚本,参数1表示启用自动清理功能。如果不添加参数或使用0,则不会清理历史备份文件。

crontab时间格式说明:

1
分钟 小时 日期 月份 星期 命令

常用的crontab时间设置示例:

1
2
3
4
5
6
7
8
9
10
11
# 每天凌晨2点执行并启用自动清理
0 2 * * * /path/to/backup_script.sh 1

# 每周日凌晨2点执行并启用自动清理
0 2 * * 0 /path/to/backup_script.sh 1

# 每月1日凌晨2点执行并启用自动清理
0 2 1 * * /path/to/backup_script.sh 1

# 每小时执行一次,但不清理历史备份
0 * * * * /path/to/backup_script.sh 0

在使用crontab时有几点需要注意:

  1. 确保脚本有执行权限:chmod +x /path/to/backup_script.sh
  2. 使用绝对路径指定脚本位置,避免路径问题
  3. 如果脚本中使用了相对路径,最好在脚本开头添加cd /path/to/script/directory命令

(二)验证定时任务

设置完成后,可以通过以下命令查看当前用户的定时任务列表:

1
crontab -l

五、备份文件管理

(一)备份压缩与加密

对于重要数据,建议对备份文件进行加密处理:

1
2
# 使用gpg加密备份文件
gpg --encrypt --recipient your_email@example.com "$BACKUP_DIR/$db-$DATE.sql.gz"

(二)创建可在任意数据库中恢复的备份

默认情况下,使用--databases参数创建的备份会包含CREATE DATABASE和USE语句,这使得备份只能恢复到原始数据库名称。如果您希望备份文件能够在任意数据库中恢复,可以对MySQL/MariaDB备份脚本做以下调整:

1
2
3
4
5
# 修改备份命令,不使用--databases参数,而是单独导出表结构和数据
mysqldump --user=$DB_USER --password=$DB_PASS --host=localhost \
--single-transaction --quick --lock-tables=false \
--no-create-db --skip-add-drop-database \
$db | gzip > "$DAILY_BACKUP_DIR/$db.sql.gz"

关键参数说明:

  • --no-create-db:不在SQL文件中包含CREATE DATABASE语句
  • --skip-add-drop-database:不在SQL文件中包含DROP DATABASE语句
  • 移除--databases参数:使备份不包含USE语句,可以导入到任意名称的数据库

恢复到任意数据库的方法:

1
2
3
4
5
# 创建目标数据库(如果不存在)
mysql -u 用户名 -p -e "CREATE DATABASE IF NOT EXISTS 目标数据库名"

# 将备份文件恢复到目标数据库
gunzip < 数据库备份文件.sql.gz | mysql -u 用户名 -p 目标数据库名

注意事项:

  1. 这种方式创建的备份文件不会包含数据库创建语句,只包含表结构和数据
  2. 如果目标数据库已存在同名表,可能会发生冲突,请谨慎操作
  3. 对于依赖特定数据库名的应用,修改数据库名后可能需要调整应用配置

PostgreSQL数据库的类似调整:

1
2
# 修改备份命令,使用-t参数只导出表内容而不包含CREATE DATABASE语句
PGPASSWORD="$DB_PASS" pg_dump -U $DB_USER -d $db -F c -b -v -t '*' -f "$DAILY_BACKUP_DIR/$db.backup"

恢复到任意PostgreSQL数据库:

1
2
3
4
5
# 创建目标数据库
createdb -U 用户名 目标数据库名

# 恢复备份到目标数据库
pg_restore -U 用户名 -d 目标数据库名 -v 备份文件.backup

(三)备份文件传输

将备份文件传输到远程服务器或云存储,可以使用以下方法:

  1. 使用rsync同步到远程服务器
1
rsync -avz $BACKUP_DIR user@remote_server:/path/to/backup/
  1. 使用rclone同步到云存储
1
2
3
4
5
6
7
8
# 安装rclone
curl https://rclone.org/install.sh | sudo bash

# 配置rclone(首次使用需要配置)
rclone config

# 同步到云存储
rclone sync $BACKUP_DIR remote:backup-folder

(四)备份恢复测试

定期测试备份文件的恢复是确保备份有效性的重要步骤:

1
2
3
4
5
# MySQL/MariaDB恢复示例
gunzip < backup_file.sql.gz | mysql -u username -p database_name

# PostgreSQL恢复示例
pg_restore -U username -d database_name -v backup_file.backup

(五).sql.gz备份文件使用指南

备份脚本生成的.sql.gz文件是通过gzip压缩的SQL备份文件,这种格式可以显著减小备份文件的大小,节省存储空间。以下是常用的.sql.gz文件操作方法:

  1. 查看备份内容(不解压)

    1
    zcat 数据库名-20240101.sql.gz | less
  2. 解压查看

    1
    gunzip -c 数据库名-20240101.sql.gz > 数据库名-20240101.sql
  3. 直接还原到数据库

如果备份时使用了--databases参数(如本文示例脚本中所示),备份文件已包含创建和使用数据库的语句,此时不需要指定数据库名

1
2
# 适用于使用了--databases参数的备份
gunzip < 数据库名-20240101.sql.gz | mysql -u 用户名 -p

如果备份时没有使用--databases参数,则需要指定要还原到的数据库名:

1
2
# 适用于没有使用--databases参数的备份
gunzip < 数据库名-20240101.sql.gz | mysql -u 用户名 -p 数据库名

1
zcat 数据库名-20240101.sql.gz | mysql -u 用户名 -p 数据库名
  1. 选择性还原

    1
    2
    3
    4
    5
    # 先解压
    gunzip -c 数据库名-20240101.sql.gz > 数据库名-20240101.sql
    # 编辑SQL文件(如果需要)
    # 然后导入
    mysql -u 用户名 -p 数据库名 < 数据库名-20240101.sql
  2. 恢复故障排查

如果恢复后数据库看起来是空的,可能有以下原因:

  • 使用了错误的数据库名称(检查备份文件名中的数据库名)
  • 将备份还原到了错误的数据库(与备份时的名称不一致)
  • 使用了不恰当的恢复命令格式(是否需要指定数据库名取决于备份命令)

可以尝试以下解决方法:

1
2
3
4
5
6
7
8
9
# 方法1:不指定数据库名称,直接恢复(适用于--databases备份)
gunzip < 数据库名-20240101.sql.gz | mysql -u 用户名 -p

# 方法2:使用--force参数
gunzip < 数据库名-20240101.sql.gz | mysql -u 用户名 -p --force [数据库名]

# 方法3:先检查备份文件内容
zcat 数据库名-20240101.sql.gz | head -100
# 查看文件中的数据库名和CREATE DATABASE语句

压缩格式的优点是节省存储空间,特别是对于大型数据库备份,同时保留了SQL文件的所有功能。

六、监控与告警

(一)备份状态监控

可以在备份脚本中添加邮件通知功能:

1
2
3
4
5
6
7
# 安装mailutils(如果尚未安装)
apt-get install mailutils # Ubuntu/Debian
# 或
yum install mailx # CentOS/RHEL

# 在脚本末尾添加邮件通知
echo "数据库备份完成,详情请查看日志" | mail -s "数据库备份状态报告-$DATE" your_email@example.com

(二)备份失败告警

在脚本中添加错误检测和告警:

1
2
3
4
# 备份失败时发送告警邮件
if [ $? -ne 0 ]; then
echo "数据库备份失败,请检查日志" | mail -s "数据库备份失败警告-$DATE" your_email@example.com
fi

七、常见问题与解决方案

(一)备份失败问题

  1. 权限不足:确保执行脚本的用户具有足够的权限访问数据库和写入备份目录。
  2. 磁盘空间不足:定期检查备份服务器的磁盘空间,确保有足够空间存储备份文件。
  3. 数据库连接问题:检查数据库连接参数是否正确,网络是否通畅。

(二)手动备份与定时备份的脚本调整

在实际使用中,我们可能会同时面临两种备份需求:

  1. 手动触发备份:需要随时手动执行脚本进行备份,希望保留所有历史备份文件。
  2. 定时自动备份:通过crontab定时执行,并自动清理过期备份文件。

为了同时满足这两种需求,我们可以对脚本做以下调整:

  1. 使用参数控制是否自动清理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

# 设置默认值:0表示不自动清理,1表示自动清理
AUTO_CLEANUP=${1:-0}

# 其他脚本内容...

# 只有在启用自动清理的情况下才删除过期备份
if [ "$AUTO_CLEANUP" -eq 1 ]; then
echo "执行清理操作,删除${BACKUP_RETAIN_DAYS}天前的备份文件..."
find $BACKUP_DIR -name "*.sql.gz" -type f -mtime +$BACKUP_RETAIN_DAYS -exec rm -f {} \;
else
echo "自动清理已禁用,保留所有备份文件"
fi
  1. 使用方法

    • 手动备份(不清理历史文件):./backup_script.sh
    • 带自动清理的备份(适用于crontab定时任务):./backup_script.sh 1
  2. 在crontab中配置

    1
    2
    # 每天凌晨2点执行备份并自动清理过期文件
    0 2 * * * /path/to/backup_script.sh 1

这种方式的优点是:

  • 使用同一个脚本文件,根据需要传递不同参数
  • 手动执行时默认不清理历史文件,便于保留重要备份
  • 定时任务执行时自动清理,避免磁盘空间占用过大
  • 每次备份都生成带时间戳的唯一文件名,防止覆盖已有备份
  1. 替代方案:创建两个不同的脚本
    如果需要更明确的区分,也可以创建两个脚本:
  • backup_manual.sh:用于手动备份,不包含清理功能
  • backup_auto.sh:用于自动备份,包含清理功能

(三)性能优化

  1. 选择合适的备份时间:在服务器负载较低的时间段进行备份。
  2. 使用增量备份:对于大型数据库,考虑使用增量备份减少备份时间和存储空间。
  3. 优化备份参数:根据数据库大小和服务器性能调整备份参数。

八、总结

本文详细介绍了如何在Linux服务器上创建自动备份数据库的脚本,并通过crontab设置定时任务实现自动化备份。一个完善的数据库备份方案应包括备份脚本创建、定时任务设置、备份文件管理、监控告警等多个方面。通过实施这些措施,可以有效保障数据安全,降低数据丢失风险。

对于生产环境,笔者建议:

  1. 实施多级备份策略,包括本地备份和异地备份
  2. 定期测试备份恢复流程,确保备份有效
  3. 根据业务需求调整备份频率和保留策略
  4. 对备份过程进行监控,及时处理备份异常

通过以上措施,可以构建一个可靠的数据库备份系统,为企业数据安全提供有力保障。