数据备份设计.md 12 KB

Cleanup 模块数据备份设计

1. 备份策略概述

1.1 备份原则

  • 安全第一:清理前必须完成数据备份
  • 选择性备份:只备份将要删除的数据,不是全量备份
  • 快速恢复:支持快速的数据恢复操作
  • 数据库存储:将备份数据直接存储到数据库中,确保可靠性

1.2 备份类型

  • 数据库备份:将INSERT语句直接存储到数据库表中(唯一支持的备份方式)

2. 备份架构设计

2.1 备份组件架构

备份系统
├── BackupLogic          # 备份核心逻辑
├── BackupService        # 备份服务接口
├── BackupExecutor       # 备份执行器
└── BackupRestorer       # 备份恢复器

2.2 备份流程

1. 分析清理任务 → 确定需要备份的数据
2. 创建备份计划 → 生成备份任务列表
3. 执行数据备份 → 将数据以INSERT语句形式存储到数据库
4. 验证备份完整性 → 确保备份数据完整
5. 记录备份信息 → 保存备份元数据
6. 执行清理操作 → 在备份完成后执行清理

3. 数据库设计扩展

3.1 SQL备份记录表 (cleanup_sql_backups)

CREATE TABLE `kku_cleanup_sql_backups` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `backup_id` bigint(20) unsigned NOT NULL COMMENT '关联备份记录ID',
  `table_name` varchar(100) NOT NULL COMMENT '表名',
  `sql_content` longtext NOT NULL COMMENT 'INSERT语句内容',
  `records_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '记录数量',
  `content_size` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '内容大小(字节)',
  `content_hash` varchar(64) DEFAULT NULL COMMENT '内容SHA256哈希',
  `backup_conditions` json DEFAULT NULL COMMENT '备份条件',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_backup_id` (`backup_id`),
  KEY `idx_table_name` (`table_name`),
  KEY `idx_records_count` (`records_count`),
  KEY `idx_created_at` (`created_at`),
  FOREIGN KEY (`backup_id`) REFERENCES `kku_cleanup_backups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='SQL备份记录表';

3.2 备份记录表 (cleanup_backups)

CREATE TABLE `kku_cleanup_backups` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `task_id` bigint(20) unsigned NOT NULL COMMENT '清理任务ID',
  `backup_name` varchar(100) NOT NULL COMMENT '备份名称',
  `backup_type` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '备份类型:1数据库备份',
  `tables_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '备份表数量',
  `records_count` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '备份记录数量',
  `backup_status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '备份状态:1进行中,2已完成,3已失败',
  `started_at` timestamp NULL DEFAULT NULL COMMENT '备份开始时间',
  `completed_at` timestamp NULL DEFAULT NULL COMMENT '备份完成时间',
  `expires_at` timestamp NULL DEFAULT NULL COMMENT '备份过期时间',
  `error_message` text COMMENT '错误信息',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_task_id` (`task_id`),
  KEY `idx_backup_status` (`backup_status`),
  KEY `idx_expires_at` (`expires_at`),
  KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='清理备份记录表';

4. 数据库备份实现

4.1 存储INSERT语句到数据库

/**
 * 备份表到数据库
 *
 * @param CleanupBackup $backup 备份记录
 * @param string $tableName 表名
 * @return array 备份结果
 */
private static function backupTableToDatabase(CleanupBackup $backup, string $tableName): array
{
    try {
        // 获取表数据
        $records = DB::table($tableName)->get();

        if ($records->isEmpty()) {
            // 即使没有数据也创建记录,记录表结构
            $sqlContent = "-- 表 {$tableName} 的数据备份\n";
            $sqlContent .= "-- 备份时间: " . now()->toDateTimeString() . "\n";
            $sqlContent .= "-- 该表无数据记录\n\n";

            // 获取表结构
            $createTable = DB::select("SHOW CREATE TABLE `{$tableName}`")[0];
            $sqlContent .= $createTable->{'Create Table'} . ";\n";

            $recordsCount = 0;
        } else {
            // 生成INSERT语句
            $sqlContent = static::generateInsertStatements($tableName, $records);
            $recordsCount = $records->count();
        }

        $contentSize = strlen($sqlContent);
        $contentHash = hash('sha256', $sqlContent);

        // 保存到数据库
        CleanupSqlBackup::create([
            'backup_id' => $backup->id,
            'table_name' => $tableName,
            'sql_content' => $sqlContent,
            'records_count' => $recordsCount,
            'content_size' => $contentSize,
            'content_hash' => $contentHash,
            'backup_conditions' => null,
        ]);

        return [
            'success' => true,
            'message' => "表 {$tableName} 数据库备份成功",
            'file_size' => $contentSize,
            'records_count' => $recordsCount,
        ];

    } catch (\Exception $e) {
        return [
            'success' => false,
            'message' => $e->getMessage(),
            'file_size' => 0,
            'records_count' => 0,
        ];
    }
}

4.2 数据库备份的优势

  • 查询快速:直接存储在数据库中,查询和检索非常快速
  • 无文件管理:不需要管理文件系统,避免文件丢失问题
  • 事务安全:利用数据库事务保证数据一致性
  • 压缩存储:数据库自动优化存储,无需额外压缩
  • 备份可靠:随数据库一起备份,不会单独丢失

5. 数据库备份恢复功能

5.1 从数据库备份恢复数据

/**
 * 从数据库备份恢复数据
 *
 * @param int $backupId 备份记录ID
 * @return bool 恢复是否成功
 */
public function restoreFromDatabaseBackup(int $backupId): bool
{
    try {
        DB::beginTransaction();

        // 获取备份记录
        $backup = CleanupBackup::find($backupId);
        if (!$backup) {
            throw new \Exception("备份记录不存在: {$backupId}");
        }

        // 获取所有SQL备份内容
        $sqlBackups = CleanupSqlBackup::where('backup_id', $backupId)->get();

        foreach ($sqlBackups as $sqlBackup) {
            // 执行SQL语句恢复数据
            $statements = $this->splitSqlStatements($sqlBackup->sql_content);

            foreach ($statements as $statement) {
                $statement = trim($statement);
                if (!empty($statement) && !str_starts_with($statement, '--')) {
                    DB::statement($statement);
                }
            }
        }

        DB::commit();

        Log::info('数据库备份恢复成功', [
            'backup_id' => $backupId,
            'tables_count' => $sqlBackups->count(),
            'total_records' => $sqlBackups->sum('records_count')
        ]);

        return true;

    } catch (\Exception $e) {
        DB::rollBack();
        Log::error('数据库备份恢复失败', [
            'backup_id' => $backupId,
            'error' => $e->getMessage()
        ]);
        return false;
    }
}

/**
 * 分割SQL语句
 *
 * @param string $sql SQL内容
 * @return array SQL语句数组
 */
private function splitSqlStatements(string $sql): array
{
    // 简单的SQL语句分割,按分号分割
    $statements = explode(';', $sql);

    return array_filter(array_map('trim', $statements));
}

6. 备份管理功能

6.1 备份清理策略

/**
 * 清理过期备份
 *
 * @return int 删除的备份数量
 */
public function cleanExpiredBackups(): int
{
    $expiredBackups = CleanupBackup::where('expires_at', '<', now())
        ->where('backup_status', 2) // 已完成的备份
        ->get();

    $deletedCount = 0;

    foreach ($expiredBackups as $backup) {
        try {
            DB::beginTransaction();

            // 删除相关的SQL备份记录
            CleanupSqlBackup::where('backup_id', $backup->id)->delete();

            // 删除备份记录
            $backup->delete();

            DB::commit();
            $deletedCount++;

            Log::info('删除过期备份成功', [
                'backup_id' => $backup->id,
                'backup_name' => $backup->backup_name
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('删除过期备份失败', [
                'backup_id' => $backup->id,
                'error' => $e->getMessage()
            ]);
        }
    }

    return $deletedCount;
}

6.2 备份验证

/**
 * 验证数据库备份完整性
 *
 * @param int $backupId 备份ID
 * @return bool 验证结果
 */
public function verifyBackupIntegrity(int $backupId): bool
{
    try {
        $backup = CleanupBackup::find($backupId);

        if (!$backup) {
            return false;
        }

        // 检查SQL备份记录是否存在
        $sqlBackupsCount = CleanupSqlBackup::where('backup_id', $backupId)->count();

        if ($sqlBackupsCount === 0) {
            return false;
        }

        // 验证每个SQL备份的内容哈希
        $sqlBackups = CleanupSqlBackup::where('backup_id', $backupId)->get();

        foreach ($sqlBackups as $sqlBackup) {
            $currentHash = hash('sha256', $sqlBackup->sql_content);
            if ($currentHash !== $sqlBackup->content_hash) {
                Log::warning('SQL备份内容哈希验证失败', [
                    'backup_id' => $backupId,
                    'sql_backup_id' => $sqlBackup->id,
                    'table_name' => $sqlBackup->table_name
                ]);
                return false;
            }
        }

        return true;

    } catch (\Exception $e) {
        Log::error('备份验证失败', [
            'backup_id' => $backupId,
            'error' => $e->getMessage()
        ]);
        return false;
    }
}

7. 配置选项

7.1 备份配置

// config/cleanup.php
return [
    'backup' => [
        // 备份类型(固定为数据库备份)
        'type' => 'database',

        // 备份保留天数
        'retention_days' => 30,

        // 分批处理大小
        'batch_size' => 1000,

        // 是否自动清理过期备份
        'auto_cleanup' => true,

        // 生成INSERT语句时的最大记录数
        'max_records_per_statement' => 100,
    ]
];

8. 总结

这个数据库备份设计专注于将备份数据直接存储到数据库中,具有以下优势:

8.1 设计优势

  • 简化架构:只支持数据库备份,架构简单清晰
  • 查询快速:备份数据存储在数据库中,查询和检索非常快速
  • 无文件管理:不需要管理文件系统,避免文件丢失问题
  • 事务安全:利用数据库事务保证数据一致性
  • 备份可靠:随数据库一起备份,不会单独丢失
  • 空间优化:数据库自动优化存储,无需额外压缩

8.2 适用场景

  • 中小型数据量的清理备份
  • 需要快速查询和恢复的场景
  • 对数据安全性要求较高的环境
  • 不希望管理复杂文件系统的场景

这个备份方案专注于数据库备份,提供了完整的数据保护机制,确保在清理数据前能够安全地备份将要删除的数据,并支持快速恢复。