Python数据API数据备份怎么策略

wen python案例 24

本文目录导读:

Python数据API数据备份怎么策略

  1. 第一阶段:策略设计(先定规则再写代码)
  2. 第二阶段:具体实现(Python脚本)
  3. 第三阶段:云存储与异地容灾(关键!)
  4. 第四阶段:恢复测试(最重要的一步)
  5. 给你的API建议

针对Python数据API的备份策略,核心目标是在数据丢失(误删、损坏、被黑)或系统故障时,能够快速恢复服务。

下面是一套从策略设计代码实现的完整方案,涵盖最常用的三种场景:JSON/CSV文件SQLite/MySQL数据库Redis缓存


第一阶段:策略设计(先定规则再写代码)

维度 推荐方案(针对小型/中型API) 说明
备份频率 全量备份:每天一次(凌晨低峰期)
增量/差异备份:每1-6小时一次
减少存储压力,同时保证RPO(恢复点目标)在1小时内
保留周期 最近7天全量 + 最近24小时增量 节省存储空间,满足大多数回滚需求
存储位置 本地磁盘 + 云存储(对象存储) 避免单点故障(如硬盘损坏)
验证机制 备份完成后自动校验MD5/SHA256 防止静默损坏
恢复演练 每月至少一次自动化恢复测试 确保备份真的可用

第二阶段:具体实现(Python脚本)

以下代码覆盖文件型数据SQLiteMySQLRedis四种常见API后端。

通用工具函数(备份文件、验证、清理旧备份)

import os
import shutil
import hashlib
import datetime
import schedule
import time
BACKUP_ROOT = "/data/backups"  # 备份根目录
RETENTION_DAYS = 7            # 保留天数
def calculate_md5(file_path):
    """计算文件MD5用于校验"""
    hash_md5 = hashlib.md5()
    with open(file_path, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()
def clean_old_backups(backup_type_dir):
    """删除指定目录下超过保留天数的备份"""
    now = time.time()
    for f in os.listdir(backup_type_dir):
        full_path = os.path.join(backup_type_dir, f)
        if os.path.isfile(full_path):
            # 从文件名解析日期,假设格式为: data_backup_20231001_120000.tar.gz
            parts = f.split('_')
            if len(parts) >= 3:
                try:
                    file_time = datetime.datetime.strptime(parts[-2] + parts[-1].split('.')[0], "%Y%m%d%H%M%S")
                    if (datetime.datetime.now() - file_time).days > RETENTION_DAYS:
                        os.remove(full_path)
                        print(f"【清理】删除过期备份: {f}")
                except ValueError:
                    pass
def verify_backup(backup_path):
    """验证备份文件完整性(可扩展为解压校验)"""
    return os.path.exists(backup_path) and os.path.getsize(backup_path) > 0

备份JSON/CSV文件型数据

def backup_json_data(api_data_path, backup_category="json"):
    """备份API使用的JSON/CSV数据文件"""
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_dir = os.path.join(BACKUP_ROOT, backup_category)
    os.makedirs(backup_dir, exist_ok=True)
    backup_filename = f"{backup_category}_backup_{timestamp}.tar.gz"
    backup_fullpath = os.path.join(backup_dir, backup_filename)
    # 压缩备份(比单纯复制节省空间)
    shutil.make_archive(backup_fullpath.replace('.tar.gz', ''), 'gztar', api_data_path)
    # 校验
    md5 = calculate_md5(backup_fullpath)
    with open(backup_fullpath + ".md5", "w") as f:
        f.write(md5)
    # 清理旧备份
    clean_old_backups(backup_dir)
    # 同步到云存储(示例:阿里云OSS/S3)
    # upload_to_oss(backup_fullpath)
    print(f"✅ 文件数据备份成功: {backup_fullpath} (MD5: {md5[:8]}...)")
    return backup_fullpath
# 使用: backup_json_data("/data/api/data/")

备份SQLite数据库(API常用轻量级库)

SQLite是文件型数据库,直接复制可能会损坏(写入中),需要使用 sqlite3 模块的逻辑备份。

import sqlite3
def backup_sqlite(db_path, backup_category="sqlite"):
    """安全的SQLite在线备份(VACUUM INTO 或 .backup)"""
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_dir = os.path.join(BACKUP_ROOT, backup_category)
    os.makedirs(backup_dir, exist_ok=True)
    backup_filename = f"{backup_category}_backup_{timestamp}.db"
    backup_fullpath = os.path.join(backup_dir, backup_filename)
    # 方法1:使用Python sqlite3 的备份API(推荐,支持热备份)
    source_conn = sqlite3.connect(db_path)
    backup_conn = sqlite3.connect(backup_fullpath)
    source_conn.backup(backup_conn, pages=1024)  # 逐页复制,不锁原库
    source_conn.close()
    backup_conn.close()
    # 校验
    md5 = calculate_md5(backup_fullpath)
    with open(backup_fullpath + ".md5", "w") as f:
        f.write(md5)
    clean_old_backups(backup_dir)
    print(f"✅ SQLite备份成功: {backup_fullpath}")
    return backup_fullpath
# 使用: backup_sqlite("/data/api/mydatabase.db")

备份MySQL数据库(使用mysqldump

import subprocess
def backup_mysql(host='localhost', user='root', password='yourpass', db_name='api_db', backup_category="mysql"):
    """使用mysqldump备份MySQL(生产环境建议用 xtrabackup 做物理备份)"""
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_dir = os.path.join(BACKUP_ROOT, backup_category)
    os.makedirs(backup_dir, exist_ok=True)
    backup_filename = f"{backup_category}_backup_{timestamp}.sql.gz"
    backup_fullpath = os.path.join(backup_dir, backup_filename)
    # 压缩导出
    dump_cmd = f"mysqldump -h{host} -u{user} -p{password} --single-transaction --routines --triggers {db_name} | gzip > {backup_fullpath}"
    try:
        subprocess.run(dump_cmd, shell=True, check=True, timeout=3600)
        md5 = calculate_md5(backup_fullpath)
        with open(backup_fullpath + ".md5", "w") as f:
            f.write(md5)
        clean_old_backups(backup_dir)
        print(f"✅ MySQL备份成功: {backup_fullpath}")
    except subprocess.CalledProcessError as e:
        print(f"❌ MySQL备份失败: {e}")
        return None
    return backup_fullpath

备份Redis缓存数据(RDB快照)

import redis
def backup_redis(redis_host='localhost', redis_port=6379, backup_category="redis"):
    """对Redis执行BGSAVE并复制RDB文件"""
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_dir = os.path.join(BACKUP_ROOT, backup_category)
    os.makedirs(backup_dir, exist_ok=True)
    client = redis.Redis(host=redis_host, port=redis_port)
    # 触发BGSAVE(异步,不阻塞服务)
    client.bgsave()
    print("⏳ Redis BGSAVE 已触发,等待完成...")
    # 等待BGSAVE完成(最多30秒)
    for _ in range(30):
        info = client.info("persistence")
        if info.get("rdb_bgsave_in_progress") == 0:
            break
        time.sleep(1)
    # 获取RDB文件路径(通常在 /var/lib/redis/dump.rdb)
    rdb_path = client.config_get("dir")["dir"] + "/dump.rdb"
    if os.path.exists(rdb_path):
        backup_filename = f"{backup_category}_backup_{timestamp}.rdb"
        backup_fullpath = os.path.join(backup_dir, backup_filename)
        shutil.copy2(rdb_path, backup_fullpath)
        md5 = calculate_md5(backup_fullpath)
        with open(backup_fullpath + ".md5", "w") as f:
            f.write(md5)
        clean_old_backups(backup_dir)
        print(f"✅ Redis备份成功: {backup_fullpath}")
        return backup_fullpath
    else:
        print("❌ Redis RDB文件未找到")
        return None

统一调度器(定时任务)

def daily_full_backup():
    """每日凌晨2点执行全量备份"""
    print(f"开始每日全量备份: {datetime.datetime.now()}")
    backup_json_data("/data/api/data/")
    backup_sqlite("/data/api/mydatabase.db")
    backup_mysql()
    backup_redis()
    print("全量备份完成")
def hourly_incremental_backup():
    """每小时增量备份(仅针对主要数据文件)"""
    print(f"开始增量备份: {datetime.datetime.now()}")
    # 增量备份方案(示例:仅备份变化文件)
    # rsync --link-dest=/data/backups/json/previous_full /data/api/data/ /data/backups/json/incremental/
    print("增量备份完成")
# 定时任务配置
schedule.every().day.at("02:00").do(daily_full_backup)
schedule.every(1).hour.do(hourly_incremental_backup)
if __name__ == "__main__":
    print("备份守护进程启动中...")
    while True:
        schedule.run_pending()
        time.sleep(60)  # 每分钟检查一次

第三阶段:云存储与异地容灾(关键!)

仅存在本地硬盘的备份是不安全的(硬盘损坏、勒索病毒)。

def sync_to_cloud(local_backup_path, cloud_path="backups/api/"):
    """同步到阿里云OSS / AWS S3(需安装boto3)"""
    import boto3
    s3 = boto3.client('s3', 
                      aws_access_key_id='your_key',
                      aws_secret_access_key='your_secret')
    s3.upload_file(local_backup_path, 'your-bucket', 
                   cloud_path + os.path.basename(local_backup_path))
    print(f"☁️ 已同步到云存储: {cloud_path}")

或者使用 rsync 到另一台服务器(增量、带压缩):

rsync -avz --delete /data/backups/ user@remote-server:/data/backups/

第四阶段:恢复测试(最重要的一步)

创建 restore_test.py,每月自动跑一次:

def test_restore_json(backup_file, restore_path="/tmp/test_restore"):
    """解压备份到临时目录,校验文件完整性"""
    shutil.rmtree(restore_path, ignore_errors=True)
    shutil.unpack_archive(backup_file, restore_path)
    # 校验MD5
    # 模拟API启动,测试读取
    print(f"✅ JSON恢复测试通过: {backup_file}")
def test_restore_sqlite(backup_db):
    """恢复SQLite并执行简单查询"""
    conn = sqlite3.connect(backup_db)
    cursor = conn.cursor()
    cursor.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table'")
    tables = cursor.fetchone()[0]
    print(f"✅ SQLite恢复测试: 包含 {tables} 个表")
    conn.close()

给你的API建议

数据规模 推荐备份策略 工具/方式
< 10MB (JSON/SQLite) 全量备份 + 同步到Git外置仓库 上面脚本 + git (二进制文件慎用)
10MB - 1GB 每日全量 + 每4小时增量 上面脚本 + rsync
> 1GB (MySQL/PostgreSQL) 物理备份 (xtrabackup / pg_basebackup) + Binlog 专业工具,不要用mysqldump
Redis RDB定期转存 + AOF日志归档 BGSAVE + 文件复制

最后提醒:不要把备份脚本和业务代码混在一起!单独部署一个 Backup Service(可以是一个独立的Python进程 + crontab),这样即使API挂了,备份依然能运行。

抱歉,评论功能暂时关闭!