📖 数据密集型设计

数据库备份恢复策略与实现

深入探讨数据库备份恢复策略与灾难恢复方案

一、备份恢复概述

数据库备份恢复是保障数据安全的核心措施,能够在数据丢失或损坏时快速恢复数据。在数据密集型应用中,建立完善的备份恢复体系是保障业务连续性的关键。

二、备份类型与策略

2.1 备份类型对比

备份类型 描述 优点 缺点 适用场景
全量备份 备份全部数据 恢复简单 备份时间长、占用空间大 定期基础备份
增量备份 备份变化的数据 备份速度快、占用空间小 恢复复杂 日常备份
差异备份 备份上次全量后变化的数据 恢复较简单 占用空间较大 中等频率备份
事务日志备份 备份事务日志 支持点恢复 依赖全量备份 高可用场景

2.2 3-2-1备份原则

graph TD A[3-2-1备份原则] --> B[3份副本] A --> C[2种介质] A --> D[1份异地] B --> B1[原始数据] B --> B2[本地备份] B --> B3[异地备份] C --> C1[磁盘] C --> C2[磁带/云存储] D --> D1[异地数据中心] D --> D2[云端备份]

三、MySQL备份恢复

3.1 MySQL备份策略

public class MySqlBackupService
{
    public async Task PerformFullBackupAsync(string backupPath)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupFile = Path.Combine(backupPath, $"full_backup_{timestamp}.sql");
        
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "mysqldump",
                Arguments = $"--single-transaction --quick --lock-tables=false -u root -p password database > {backupFile}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task PerformIncrementalBackupAsync(string backupPath)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupFile = Path.Combine(backupPath, $"incremental_backup_{timestamp}.binlog");
        
        await _mysqlClient.ExecuteAsync("FLUSH LOGS");
        await _mysqlClient.CopyBinlogToBackup(backupFile);
    }
    
    public async Task RestoreFromFullBackupAsync(string backupFile, string databaseName)
    {
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "mysql",
                Arguments = $"-u root -p password {databaseName} < {backupFile}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task RestorePointInTimeAsync(string fullBackupFile, string binlogFile, DateTime pointInTime)
    {
        await RestoreFromFullBackupAsync(fullBackupFile, "temp_database");
        
        await _mysqlClient.ExecuteAsync($"mysqlbinlog --stop-datetime='{pointInTime:yyyy-MM-dd HH:mm:ss}' {binlogFile} | mysql -u root -p password temp_database");
    }
}

3.2 XtraBackup备份

public class XtraBackupService
{
    public async Task PerformHotBackupAsync(string backupPath)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupDir = Path.Combine(backupPath, $"xtrabackup_{timestamp}");
        
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "xtrabackup",
                Arguments = $"--backup --user=root --password=password --target-dir={backupDir}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task PrepareBackupAsync(string backupDir)
    {
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "xtrabackup",
                Arguments = $"--prepare --target-dir={backupDir}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task RestoreFromBackupAsync(string backupDir, string dataDir)
    {
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "xtrabackup",
                Arguments = $"--copy-back --target-dir={backupDir} --datadir={dataDir}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
}

四、PostgreSQL备份恢复

4.1 PostgreSQL备份策略

public class PostgreSqlBackupService
{
    public async Task PerformFullBackupAsync(string backupPath)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupFile = Path.Combine(backupPath, $"pg_dump_{timestamp}.dump");
        
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "pg_dump",
                Arguments = $"-U postgres -F c -b -v -f {backupFile} database",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task PerformBaseBackupAsync(string backupPath)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupDir = Path.Combine(backupPath, $"base_backup_{timestamp}");
        
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "pg_basebackup",
                Arguments = $"-U postgres -D {backupDir} -F tar -z -P",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task RestoreFromBackupAsync(string backupFile, string databaseName)
    {
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "pg_restore",
                Arguments = $"-U postgres -d {databaseName} -v {backupFile}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task EnableWalArchivingAsync(string archiveDir)
    {
        await _postgreSqlClient.ExecuteAsync($"ALTER SYSTEM SET wal_level = 'replica';");
        await _postgreSqlClient.ExecuteAsync($"ALTER SYSTEM SET archive_mode = 'on';");
        await _postgreSqlClient.ExecuteAsync($"ALTER SYSTEM SET archive_command = 'cp %p {archiveDir}/%f';");
        
        await _postgreSqlClient.RestartAsync();
    }
}

五、MongoDB备份恢复

5.1 MongoDB备份策略

public class MongoDbBackupService
{
    public async Task PerformFullBackupAsync(string backupPath)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupDir = Path.Combine(backupPath, $"mongodump_{timestamp}");
        
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "mongodump",
                Arguments = $"--uri=mongodb://localhost:27017 --out={backupDir}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task PerformPointInTimeBackupAsync(string backupPath, DateTime pointInTime)
    {
        var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
        var backupDir = Path.Combine(backupPath, $"pitr_backup_{timestamp}");
        
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "mongodump",
                Arguments = $"--uri=mongodb://localhost:27017 --out={backupDir} --oplog --stopTime={pointInTime:yyyy-MM-ddTHH:mm:ssZ}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task RestoreFromBackupAsync(string backupDir)
    {
        using var process = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = "mongorestore",
                Arguments = $"--uri=mongodb://localhost:27017 {backupDir}",
                UseShellExecute = true,
                CreateNoWindow = true
            }
        };
        
        process.Start();
        await process.WaitForExitAsync();
    }
    
    public async Task EnableOplogAsync()
    {
        await _mongoClient.GetDatabase("admin").RunCommandAsync(
            BsonDocument.Parse("{ replSetInitiate: { _id: 'rs0', members: [ { _id: 0, host: 'localhost:27017' } ] } }"));
    }
}

六、灾难恢复方案

6.1 灾难恢复架构

graph TD A[生产数据中心] --> B[主数据库] B --> C[从数据库] B --> D[备份服务器] A --> E[网络故障] A --> F[自然灾害] G[异地灾备中心] --> H[灾备数据库] H --> I[灾备应用服务器] B --> J[数据同步] J --> H D --> K[备份同步] K --> G E --> L[切换到从库] F --> M[切换到灾备中心]

6.2 RTO与RPO

指标 定义 影响因素 优化策略
RTO 恢复时间目标 恢复流程复杂度 自动化恢复
RPO 恢复点目标 备份频率 实时同步

6.3 灾难恢复流程

public class DisasterRecoveryService
{
    public async Task ExecuteDisasterRecoveryAsync(RecoveryPlan plan)
    {
        await ValidateDisasterAsync();
        await SwitchToBackupAsync(plan);
        await VerifyRecoveryAsync();
        await NotifyStakeholdersAsync();
    }
    
    private async Task ValidateDisasterAsync()
    {
        var healthStatus = await _healthCheckService.CheckAllAsync();
        
        if (!healthStatus.IsHealthy)
        {
            await _alertService.SendAlert("检测到灾难", 
                $"健康状态: {healthStatus.Details}");
        }
    }
    
    private async Task SwitchToBackupAsync(RecoveryPlan plan)
    {
        await _dnsService.SwitchToBackupDNS();
        await _loadBalancerService.SwitchToBackupServers();
        await _databaseService.FailoverToBackup();
    }
    
    private async Task VerifyRecoveryAsync()
    {
        var verificationResult = await _verificationService.VerifyAsync();
        
        if (!verificationResult.IsSuccessful)
        {
            await _alertService.SendAlert("恢复验证失败", 
                $"失败原因: {verificationResult.FailureReason}");
        }
    }
    
    private async Task NotifyStakeholdersAsync()
    {
        await _notificationService.SendNotification("灾难恢复完成", 
            "系统已切换到备份环境");
    }
}

public class RecoveryPlan
{
    public string PlanName { get; set; }
    public RecoveryType Type { get; set; }
    public string TargetEnvironment { get; set; }
    public int ExpectedRtoMinutes { get; set; }
}

public enum RecoveryType
{
    Failover,
    Restore,
    Rebuild
}

七、备份监控与验证

7.1 备份监控

public class BackupMonitor
{
    public async Task CheckBackupStatusAsync()
    {
        var lastFullBackup = await _backupRepository.GetLastBackupAsync(BackupType.Full);
        var lastIncrementalBackup = await _backupRepository.GetLastBackupAsync(BackupType.Incremental);
        
        var fullBackupAge = DateTime.Now - lastFullBackup.CreatedAt;
        var incrementalBackupAge = DateTime.Now - lastIncrementalBackup.CreatedAt;
        
        return new BackupStatus
        {
            LastFullBackup = lastFullBackup,
            LastIncrementalBackup = lastIncrementalBackup,
            FullBackupHealthy = fullBackupAge < TimeSpan.FromDays(7),
            IncrementalBackupHealthy = incrementalBackupAge < TimeSpan.FromHours(24),
            StorageUsage = await _storageService.GetUsageAsync()
        };
    }
    
    public async Task MonitorAsync()
    {
        var status = await CheckBackupStatusAsync();
        
        if (!status.FullBackupHealthy)
        {
            await _alertService.SendAlert("全量备份过期", 
                $"上次全量备份: {status.LastFullBackup.CreatedAt}");
        }
        
        if (!status.IncrementalBackupHealthy)
        {
            await _alertService.SendAlert("增量备份过期", 
                $"上次增量备份: {status.LastIncrementalBackup.CreatedAt}");
        }
    }
}

public class BackupStatus
{
    public BackupInfo LastFullBackup { get; set; }
    public BackupInfo LastIncrementalBackup { get; set; }
    public bool FullBackupHealthy { get; set; }
    public bool IncrementalBackupHealthy { get; set; }
    public StorageUsage StorageUsage { get; set; }
}

7.2 备份验证

public class BackupValidator
{
    public async Task ValidateBackupAsync(string backupFile)
    {
        var checks = new List>
        {
            CheckFileExistsAsync(backupFile),
            CheckFileIntegrityAsync(backupFile),
            CheckFileSizeAsync(backupFile),
            CheckRestoreTestAsync(backupFile)
        };
        
        var results = await Task.WhenAll(checks);
        
        return new ValidationResult
        {
            AllPassed = results.All(r => r.Passed),
            Checks = results
        };
    }
    
    private async Task CheckFileExistsAsync(string backupFile)
    {
        var exists = File.Exists(backupFile);
        
        return new CheckResult { Name = "文件存在", Passed = exists };
    }
    
    private async Task CheckFileIntegrityAsync(string backupFile)
    {
        var hash = await CalculateFileHashAsync(backupFile);
        var expectedHash = await GetExpectedHashAsync(backupFile);
        
        return new CheckResult { Name = "文件完整性", Passed = hash == expectedHash };
    }
    
    private async Task CheckRestoreTestAsync(string backupFile)
    {
        var testDatabase = $"test_restore_{Guid.NewGuid():N}";
        
        try
        {
            await _backupService.RestoreFromBackupAsync(backupFile, testDatabase);
            await _databaseService.VerifyDatabaseAsync(testDatabase);
            
            return new CheckResult { Name = "恢复测试", Passed = true };
        }
        finally
        {
            await _databaseService.DropDatabaseAsync(testDatabase);
        }
    }
}

八、备份恢复最佳实践

8.1 制定备份策略

public class BackupPolicy
{
    public BackupType Type { get; set; }
    public Schedule Schedule { get; set; }
    public RetentionPolicy Retention { get; set; }
    public EncryptionPolicy Encryption { get; set; }
}

public enum BackupType
{
    Full,
    Incremental,
    Differential,
    Log
}

public class Schedule
{
    public string CronExpression { get; set; }
    public TimeZoneInfo TimeZone { get; set; }
}

public class RetentionPolicy
{
    public TimeSpan FullBackupRetention { get; set; }
    public TimeSpan IncrementalBackupRetention { get; set; }
}

public class EncryptionPolicy
{
    public bool Enabled { get; set; }
    public string Algorithm { get; set; }
}

8.2 自动化备份

public class BackupScheduler
{
    public void ScheduleBackups(BackupPolicy policy)
    {
        var scheduler = new HangfireBackgroundJobScheduler();
        
        scheduler.ScheduleRecurringJob(
            () => _backupService.PerformFullBackupAsync(policy),
            "full_backup",
            policy.Schedule.CronExpression);
        
        scheduler.ScheduleRecurringJob(
            () => _backupService.PerformIncrementalBackupAsync(policy),
            "incremental_backup",
            "0 * * * *");
        
        scheduler.ScheduleRecurringJob(
            () => _backupValidator.ValidateBackupAsync(),
            "validate_backup",
            "0 2 * * *");
    }
}

8.3 定期演练恢复

定期进行灾难恢复演练,确保备份可用且恢复流程顺畅。

8.4 加密备份数据

public class BackupEncryptionService
{
    public async Task EncryptBackupAsync(string backupFile, string encryptionKey)
    {
        var encryptedFile = $"{backupFile}.encrypted";
        
        using var inputStream = File.OpenRead(backupFile);
        using var outputStream = File.Create(encryptedFile);
        using var aes = Aes.Create();
        
        aes.Key = Convert.FromBase64String(encryptionKey);
        aes.GenerateIV();
        
        outputStream.Write(aes.IV, 0, aes.IV.Length);
        
        using var cryptoStream = new CryptoStream(outputStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
        await inputStream.CopyToAsync(cryptoStream);
        
        return encryptedFile;
    }
    
    public async Task DecryptBackupAsync(string encryptedFile, string encryptionKey)
    {
        var decryptedFile = encryptedFile.Replace(".encrypted", "");
        
        using var inputStream = File.OpenRead(encryptedFile);
        
        var iv = new byte[16];
        await inputStream.ReadAsync(iv, 0, iv.Length);
        
        using var outputStream = File.Create(decryptedFile);
        using var aes = Aes.Create();
        
        aes.Key = Convert.FromBase64String(encryptionKey);
        aes.IV = iv;
        
        using var cryptoStream = new CryptoStream(inputStream, aes.CreateDecryptor(), CryptoStreamMode.Read);
        await cryptoStream.CopyToAsync(outputStream);
        
        return decryptedFile;
    }
}

九、总结

数据库备份恢复是数据密集型应用中保障数据安全的核心措施。遵循3-2-1备份原则,选择合适的备份策略,建立完善的灾难恢复方案,能够在数据丢失或损坏时快速恢复数据。定期监控和验证备份,确保备份可用且恢复流程顺畅。