一、技术背景与应用场景

对于使用PostgreSQL作为数据存储的C#开发者而言,数据库的备份与恢复是系统运维的关键环节。Npgsql作为.NET平台最成熟的PostgreSQL驱动程序,不仅能执行常规CRUD操作,还能通过其丰富的扩展功能实现数据库的备份与恢复。

典型应用场景包括:

  • 生产环境定期全量备份
  • 开发测试环境快速数据克隆
  • 数据库迁移时的数据转移
  • 灾难恢复演练
  • CI/CD流程中的数据库版本管理

二、Npgsql备份方案实现

2.1 逻辑备份方案(使用pg_dump)

using System.Diagnostics;
using Npgsql;

public class DatabaseBackupService
{
    // pg_dump路径(需根据实际安装位置调整)
    private const string PgDumpPath = @"C:\Program Files\PostgreSQL\15\bin\pg_dump.exe";
    
    public void PerformLogicalBackup(string connectionString, string outputPath)
    {
        var builder = new NpgsqlConnectionStringBuilder(connectionString);
        
        // 构建pg_dump命令行参数
        var arguments = $"-h {builder.Host} -p {builder.Port} " +
                        $"-U {builder.Username} -d {builder.Database} " +
                        $"-Fc -f \"{outputPath}\"";

        var processStartInfo = new ProcessStartInfo
        {
            FileName = PgDumpPath,
            Arguments = arguments,
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        };
        
        // 设置环境变量(避免密码明文出现在命令行)
        processStartInfo.EnvironmentVariables["PGPASSWORD"] = builder.Password;

        using var process = Process.Start(processStartInfo);
        process.WaitForExit();
        
        if (process.ExitCode != 0)
        {
            var error = process.StandardError.ReadToEnd();
            throw new Exception($"备份失败:{error}");
        }
    }
}

// 使用示例:
var service = new DatabaseBackupService();
service.PerformLogicalBackup(
    "Host=localhost;Port=5432;Username=postgres;Password=123456;Database=mydb",
    @"D:\backups\mydb_20230815.dump");

方案特点:

  • 使用PostgreSQL官方工具pg_dump实现
  • 支持全库/单表备份
  • 生成压缩的自定义格式文件(-Fc)
  • 需要数据库服务可访问

2.2 物理备份方案(文件系统级备份)

using Npgsql;
using System.IO;

public class PhysicalBackupService
{
    public void CreatePhysicalBackup(string dataDirectory, string backupPath)
    {
        // 通过Npgsql执行检查点
        using var conn = new NpgsqlConnection("Host=localhost;Username=postgres;Password=123456");
        conn.Open();
        using var cmd = new NpgsqlCommand("CHECKPOINT", conn);
        cmd.ExecuteNonQuery();

        // 复制数据库文件
        Directory.CreateDirectory(backupPath);
        CopyDirectory(dataDirectory, backupPath);
    }

    private void CopyDirectory(string sourceDir, string targetDir)
    {
        foreach (var file in Directory.GetFiles(sourceDir))
        {
            File.Copy(file, Path.Combine(targetDir, Path.GetFileName(file)), true);
        }
        
        foreach (var dir in Directory.GetDirectories(sourceDir))
        {
            var newDir = Path.Combine(targetDir, Path.GetFileName(dir));
            Directory.CreateDirectory(newDir);
            CopyDirectory(dir, newDir);
        }
    }
}

// 使用示例(需先停止PostgreSQL服务):
var service = new PhysicalBackupService();
service.CreatePhysicalBackup(
    @"C:\Program Files\PostgreSQL\15\data",
    @"D:\backups\physical_20230815");

方案特点:

  • 直接备份数据库文件
  • 需要数据库停止服务或处于备份模式
  • 备份速度更快
  • 文件体积较大

三、数据库恢复方案实现

3.1 逻辑备份恢复

using System.Diagnostics;

public class DatabaseRestoreService
{
    private const string PgRestorePath = @"C:\Program Files\PostgreSQL\15\bin\pg_restore.exe";

    public void RestoreFromDump(string connectionString, string dumpPath)
    {
        var builder = new NpgsqlConnectionStringBuilder(connectionString);
        
        var arguments = $"-h {builder.Host} -p {builder.Port} " +
                        $"-U {builder.Username} -d {builder.Database} " +
                        $"-c -Fc \"{dumpPath}\"";

        var processStartInfo = new ProcessStartInfo
        {
            FileName = PgRestorePath,
            Arguments = arguments,
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        };
        
        processStartInfo.EnvironmentVariables["PGPASSWORD"] = builder.Password;

        using var process = Process.Start(processStartInfo);
        process.WaitForExit();
        
        if (process.ExitCode != 0)
        {
            var error = process.StandardError.ReadToEnd();
            throw new Exception($"恢复失败:{error}");
        }
    }
}

// 使用示例:
var restoreService = new DatabaseRestoreService();
restoreService.RestoreFromDump(
    "Host=localhost;Port=5432;Username=postgres;Password=123456;Database=newdb",
    @"D:\backups\mydb_20230815.dump");

3.2 物理备份恢复

public class PhysicalRestoreService
{
    public void RestorePhysicalBackup(string backupPath, string dataDirectory)
    {
        // 停止PostgreSQL服务(此处需要调用系统服务管理接口)
        StopPostgreService();
        
        // 清空现有数据目录
        Directory.Delete(dataDirectory, true);
        Directory.CreateDirectory(dataDirectory);
        
        // 恢复备份文件
        CopyDirectory(backupPath, dataDirectory);
        
        // 启动PostgreSQL服务
        StartPostgreService();
    }

    // 实现服务启停的方法需要根据实际环境实现
    private void StopPostgreService() { /* ... */ }
    private void StartPostgreService() { /* ... */ }
}

四、技术方案对比分析

4.1 逻辑备份 vs 物理备份

对比维度 逻辑备份 物理备份
备份速度 较慢 快速
备份体积 较小 较大
恢复灵活性 支持跨版本恢复 必须同版本恢复
是否需要停机 不需要 需要
备份粒度 表/数据库级 全实例级
版本兼容性 较好 严格

4.2 方案选择建议

  • 开发环境:推荐逻辑备份,便于快速恢复单表数据
  • 生产环境:物理备份+逻辑备份组合使用
  • 大数据量场景:优先考虑物理备份
  • 跨版本迁移:必须使用逻辑备份

五、注意事项与最佳实践

5.1 通用注意事项

  1. 权限管理
// 确保运行账号具有文件系统访问权限
var hasAccess = new FileIOPermission(FileIOPermissionAccess.Write, backupPath).IsUnrestricted();
  1. 资源监控
// 监控备份过程中的资源占用
var startCpu = Process.GetCurrentProcess().TotalProcessorTime;
var startMem = Process.GetCurrentProcess().WorkingSet64;
  1. 版本验证
var versionCmd = new NpgsqlCommand("SELECT version()", conn);
var version = versionCmd.ExecuteScalar().ToString();

5.2 进阶优化技巧

  • 使用SSH隧道进行远程备份
  • 结合Barman进行物理备份管理
  • 实现增量备份策略
  • 集成Azure Blob Storage等云存储