如何编写通用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
相关产品推荐
相关产品推荐

