数据库备份恢复自动化:脚本编写与定时任务配置

数据库备份自动化不只是写个脚本加个 cron。本文从备份脚本的可靠性、日志记录、定时任务配置、失败处理等角度,给出可落地的操作步骤与取舍,并指出常见误区。

数据库备份恢复自动化:脚本编写与定时任务配置
封面图:ZuCDN · ZuCDN 原创

数据库备份自动化是运维的常见需求,但很多团队只是简单写个脚本丢进 cron,结果备份失败、日志丢失、恢复时才发现备份不可用。本文先明确自动化的边界,再给出可执行的脚本编写与定时任务配置方案,帮助你避开常见坑。

先明确:备份自动化能做什么,不能做什么

自动化可以按时执行备份命令、记录日志、清理旧备份、发送失败告警。但它不能保证备份一定可恢复——这取决于备份工具、存储介质和恢复演练。因此,自动化脚本应当包含校验步骤(如备份文件大小、校验和),并定期做恢复测试。本文不涉及具体数据库的备份命令,但会给出通用的脚本框架。

编写可靠的备份脚本:核心要点

一个可靠的备份脚本至少需要处理:备份命令执行、退出码检查、日志记录、失败时告警。以下以 Python 为例,因为 Python 标准库提供了灵活的日志模块,且跨平台。

使用日志模块记录备份过程

根据 Python Logging 官方文档,日志模块支持分层记录,建议在脚本中创建模块级 logger,并配置 FileHandler 和 StreamHandler。这样既能输出到控制台,也能写入日志文件。例如:

import logging
logging.basicConfig(
    filename='backup.log',
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(message)s'
)
logger = logging.getLogger(__name__)
logger.info('备份开始')

日志应记录开始时间、结束时间、备份文件路径、大小、校验和等关键信息。OWASP 日志安全速查表指出,日志应包含事件时间、来源、结果等,但避免记录敏感数据(如密码),因此备份脚本日志中不要包含数据库连接字符串或口令。

检查退出码,不只是看命令是否执行

备份命令返回非零退出码时,脚本必须捕获并处理。在 shell 中可以用 set -e,但在 Python 中更推荐使用 subprocess 模块并检查 returncode。例如:

import subprocess
result = subprocess.run(['pg_dump', 'dbname'], capture_output=True)
if result.returncode != 0:
    logger.error('备份失败: %s', result.stderr)
    exit(1)
else:
    logger.info('备份成功')

备份后立即校验文件

备份文件生成后,应检查其是否存在且大小非零。更严谨的做法是计算校验和(如 md5)并记录,以便恢复时验证完整性。若备份工具支持,可启用压缩和加密,但要注意压缩后的文件大小与时间。

配置定时任务:cron 与 systemd timer 的选择

Linux 下最常用的是 cron,但 systemd timer 提供了更精细的控制和日志集成。选择取决于你的环境:如果服务器使用 systemd,推荐使用 timer;否则 cron 更通用。

cron 配置示例

# 每天凌晨 2 点执行备份脚本
0 2 * * * /usr/local/bin/backup.py --config /etc/backup.conf

注意 cron 环境变量有限,脚本中应使用绝对路径,并设置 PATH。cron 执行失败时,默认会发送邮件给 root,但很多服务器未配置邮件服务,因此建议在脚本中自行实现告警(如写入日志文件或调用 Webhook)。

systemd timer 配置示例

创建 service 和 timer 单元:

# /etc/systemd/system/backup.service
[Unit]
Description=Database backup
[Service]
ExecStart=/usr/local/bin/backup.py
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target

启用 timer:systemctl enable --now backup.timer。systemd 会记录服务的标准输出和错误,可通过 journalctl -u backup 查看,日志整合更方便。

处理备份失败:告警与重试策略

备份失败必须及时通知。最简单的方法是在脚本中发送邮件,但现代运维更常用 Webhook(如钉钉、Slack)。注意不要将敏感信息放入告警消息。

重试策略:对于网络或临时锁导致的失败,可以尝试重试 1-2 次,但要注意避免重复备份造成混乱。建议在日志中记录重试次数,并在最终失败时触发告警。

常见误区与注意事项

  • 只备份数据文件,不备份日志:对于数据库,备份日志(如 WAL)是恢复的关键,但很多脚本只做全量备份而忽略增量。可参考 增量备份与差异备份的区别
  • 忽略备份文件的权限:备份文件包含敏感数据,应设置严格的文件权限(如 600),并确保存储目录只有备份用户可读。
  • 不测试恢复:备份自动化只是第一步,定期演练恢复流程才能确保备份可用。可参考 备份文件损坏后的修复方法
  • 日志过多或过少:日志应记录关键事件,但避免记录敏感信息。OWASP 建议只记录必要的信息,并确保日志不被篡改。

脚本示例:一个简单的 Python 备份框架

以下是一个结合日志和定时任务的示例,假设使用 PostgreSQL 的 pg_dump:

import logging, subprocess, datetime, hashlib
logging.basicConfig(filename='/var/log/backup.log', level=logging.INFO)
logger = logging.getLogger('backup')

def md5(file_path):
    h = hashlib.md5()
    with open(file_path, 'rb') as f:
        for chunk in iter(lambda: f.read(4096), b""):
            h.update(chunk)
    return h.hexdigest()

backup_file = f'/backup/db_{datetime.datetime.now():%Y%m%d_%H%M%S}.sql'
logger.info('开始备份')
try:
    result = subprocess.run(['pg_dump', 'mydb', '-f', backup_file], capture_output=True, text=True)
    if result.returncode == 0:
        checksum = md5(backup_file)
        logger.info(f'备份成功: {backup_file} MD5: {checksum}')
    else:
        logger.error(f'备份失败: {result.stderr}')
        raise SystemExit(1)
except Exception as e:
    logger.exception('备份异常')
    raise

注意:此示例省略了配置管理、清理旧备份等,实际使用时需扩展。

总结与进一步思考

数据库备份恢复自动化涉及脚本编写、定时任务、日志与告警等多个环节。本文给出的方案是通用的,但具体数据库(如 MySQL、PostgreSQL、MongoDB)的备份命令和参数不同,需要结合官方文档调整。此外,备份自动化不是一劳永逸,应定期审查日志、测试恢复,并关注存储容量。若遇到备份失败或恢复问题,可参考 常见故障排查指南

参考资料

延伸阅读