You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写通用C#类实现Redis任意类型键的备份与恢复

Redis全类型键备份与恢复通用C#实现

需求背景

现有C#代码仅能实现Redis字符串类型键的备份与恢复,需扩展为支持所有Redis数据类型(包括列表、集合、哈希、有序集合、流、地理空间索引、位图、位域等)的单个键或键模式备份恢复操作。

原参考代码(仅支持字符串类型)

using System;
using StackExchange.Redis;
using System.IO;

class RedisKeyBackupRestore
{
    private readonly ConnectionMultiplexer _redis;
    private readonly IDatabase _db;
    private readonly string _backupFile;

    public RedisKeyBackupRestore(string connectionString, int db, string backupFile)
    {
        _redis = ConnectionMultiplexer.Connect(connectionString);
        _db = _redis.GetDatabase(db);
        _backupFile = backupFile;
    }

    public void BackupKey(string key) // 键可对应任意类型,但仅处理字符串值
    {
        var value = _db.StringGet(key); // 集合类型需使用_db.SetMembers(key)等对应方法
        using (var file = new StreamWriter(_backupFile))
        {
            file.WriteLine($"{key},{value}");
        }
    }

    public void RestoreKey(string key)
    {
        using (var file = new StreamReader(_backupFile))
        {
            var line = file.ReadLine();
            var parts = line.Split(',');
            var restoredKey = parts[0];
            var restoredValue = parts[1];
            _db.StringSet(restoredKey, restoredValue);
        }
    }
}

通用实现方案

核心思路:先获取键的Redis数据类型,根据类型调用对应读写接口;使用JSON序列化存储类型标识与数据,避免分隔符冲突;支持单键或模式匹配备份。

完整通用代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using StackExchange.Redis;

class RedisUniversalBackupRestore
{
    private readonly ConnectionMultiplexer _redis;
    private readonly IDatabase _db;
    private readonly string _backupDir;

    // 备份数据结构:存储键名、数据类型、对应数据
    private class BackupEntry
    {
        public string Key { get; set; }
        public RedisType Type { get; set; }
        public object Data { get; set; }
    }

    public RedisUniversalBackupRestore(string connectionString, int db, string backupDir)
    {
        _redis = ConnectionMultiplexer.Connect(connectionString);
        _db = _redis.GetDatabase(db);
        _backupDir = backupDir;
        Directory.CreateDirectory(backupDir);
    }

    /// <summary>
    /// 备份单个键
    /// </summary>
    public void BackupSingleKey(string key)
    {
        var keyRedis = (RedisKey)key;
        var type = _db.KeyType(keyRedis);
        var entry = new BackupEntry { Key = key, Type = type };

        // 根据数据类型读取对应数据
        switch (type)
        {
            case RedisType.String:
                entry.Data = _db.StringGet(keyRedis);
                break;
            case RedisType.List:
                entry.Data = _db.ListRange(keyRedis);
                break;
            case RedisType.Set:
                entry.Data = _db.SetMembers(keyRedis);
                break;
            case RedisType.SortedSet:
                entry.Data = _db.SortedSetRangeByRankWithScores(keyRedis);
                break;
            case RedisType.Hash:
                entry.Data = _db.HashGetAll(keyRedis);
                break;
            case RedisType.Stream:
                entry.Data = _db.StreamRead(keyRedis, StreamPosition.Begin);
                break;
            case RedisType.Geospatial:
                entry.Data = _db.GeoPosition(keyRedis, _db.GeoMembers(keyRedis));
                break;
            case RedisType.Bitmap:
                // 备份位图长度与所有位值(大位图可分段处理优化内存)
                var length = _db.StringLength(keyRedis);
                entry.Data = new { Length = length, Bits = _db.StringGet(keyRedis) };
                break;
            case RedisType.HyperLogLog:
                entry.Data = _db.StringGet(keyRedis);
                break;
            default:
                throw new NotSupportedException($"不支持的数据类型:{type}");
        }

        // 序列化到文件
        var filePath = Path.Combine(_backupDir, $"{key}.backup.json");
        File.WriteAllText(filePath, JsonSerializer.Serialize(entry, new JsonSerializerOptions { WriteIndented = true }));
    }

    /// <summary>
    /// 备份匹配模式的所有键(推荐用Scan避免阻塞Redis)
    /// </summary>
    public void BackupKeysByPattern(string pattern)
    {
        var server = _redis.GetServer(_redis.GetEndPoints()[0]);
        foreach (var key in server.Keys(_db.Database, pattern))
        {
            BackupSingleKey(key);
        }
    }

    /// <summary>
    /// 恢复单个键
    /// </summary>
    public void RestoreSingleKey(string key)
    {
        var filePath = Path.Combine(_backupDir, $"{key}.backup.json");
        if (!File.Exists(filePath))
            throw new FileNotFoundException($"未找到键{key}的备份文件");

        var json = File.ReadAllText(filePath);
        var entry = JsonSerializer.Deserialize<BackupEntry>(json);
        var keyRedis = (RedisKey)entry.Key;

        // 先删除原有键(可选,根据业务需求调整)
        _db.KeyDelete(keyRedis);

        // 根据数据类型恢复数据
        switch (entry.Type)
        {
            case RedisType.String:
                _db.StringSet(keyRedis, (RedisValue)entry.Data);
                break;
            case RedisType.List:
                _db.ListRightPush(keyRedis, (RedisValue[])entry.Data);
                break;
            case RedisType.Set:
                _db.SetAdd(keyRedis, (RedisValue[])entry.Data);
                break;
            case RedisType.SortedSet:
                var sortedSetEntries = (SortedSetEntry[])entry.Data;
                _db.SortedSetAdd(keyRedis, sortedSetEntries);
                break;
            case RedisType.Hash:
                var hashEntries = (HashEntry[])entry.Data;
                _db.HashSet(keyRedis, hashEntries);
                break;
            case RedisType.Stream:
                var streamEntries = (StreamEntry[])entry.Data;
                foreach (var streamEntry in streamEntries)
                {
                    _db.StreamAdd(keyRedis, streamEntry.Fields);
                }
                break;
            case RedisType.Geospatial:
                var geoPositions = (KeyValuePair<RedisValue, GeoPosition>[])entry.Data;
                foreach (var pos in geoPositions)
                {
                    _db.GeoAdd(keyRedis, pos.Value.Longitude, pos.Value.Latitude, pos.Key);
                }
                break;
            case RedisType.Bitmap:
                var bitmapData = JsonSerializer.Deserialize<dynamic>(entry.Data.ToString());
                _db.StringSet(keyRedis, (RedisValue)bitmapData.Bits, (long)bitmapData.Length);
                break;
            case RedisType.HyperLogLog:
                _db.StringSet(keyRedis, (RedisValue)entry.Data);
                break;
            default:
                throw new NotSupportedException($"不支持的数据类型:{entry.Type}");
        }
    }

    /// <summary>
    /// 恢复备份目录下的所有键
    /// </summary>
    public void RestoreAllKeys()
    {
        foreach (var file in Directory.GetFiles(_backupDir, "*.backup.json"))
        {
            var key = Path.GetFileNameWithoutExtension(file).Replace(".backup", "");
            RestoreSingleKey(key);
        }
    }
}

使用示例

// 初始化备份恢复工具
var backupTool = new RedisUniversalBackupRestore("localhost:6379", 0, "./redis_backups");

// 备份单个键
backupTool.BackupSingleKey("my_user_list");

// 备份所有以order_开头的键
backupTool.BackupKeysByPattern("order_*");

// 恢复单个键
backupTool.RestoreSingleKey("my_user_list");

// 恢复所有备份的键
backupTool.RestoreAllKeys();

注意事项

  • 对于超大键(如百万级列表、海量流数据),建议分段读取/写入,避免内存占用过高
  • 流数据备份默认读取全量历史,若需增量备份需额外处理ID标记逻辑
  • 生产环境模式匹配键时,优先使用Scan而非Keys命令,避免阻塞Redis服务
  • 位图备份针对小数据量场景,大位图需实现分段备份逻辑优化性能

内容的提问来源于stack exchange,提问作者ca9163d9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:20:22