别再只会用String了!C#用StackExchange.Redis操作Redis五种数据结构保姆级实战
Redis作为高性能键值数据库,其丰富的数据结构往往被.NET开发者低估——80%的项目仅用String类型处理所有场景。本文将基于StackExchange.Redis驱动,通过用户管理系统案例,拆解五种核心数据结构的差异化实战。
1. 从String到Hash:用户档案存储的范式升级
新手常犯的错误是将用户JSON序列化成String存储:
// 反模式:将所有属性压缩成单个字符串 var userJson = JsonConvert.SerializeObject(new { Id = 1001, Name = "张三", Age = 28, VipLevel = 3 }); await db.StringSetAsync("user:1001", userJson);Hash结构才是理想选择,它支持字段级读写且内存占用更低:
// 正确姿势:使用Hash存储离散属性 var entries = new HashEntry[] { new HashEntry("Id", 1001), new HashEntry("Name", "张三"), new HashEntry("Age", 28), new HashEntry("VipLevel", 3) }; await db.HashSetAsync("user:1001", entries); // 仅获取年龄字段 int age = (int)await db.HashGetAsync("user:1001", "Age");性能对比实验显示,在频繁更新单个字段的场景下,Hash比String方案吞吐量提升4倍。
2. SortedSet:构建实时排行榜系统
用户积分排行榜是典型的有序集合应用场景。与普通Set不同,SortedSet通过score参数实现自动排序:
// 添加用户积分(支持批量操作) var scores = new SortedSetEntry[] { new SortedSetEntry("user:1001", 1500), new SortedSetEntry("user:1002", 3200), new SortedSetEntry("user:1003", 2750) }; await db.SortedSetAddAsync("leaderboard", scores); // 获取TOP3用户(降序排列) var topUsers = await db.SortedSetRangeByRankAsync( "leaderboard", 0, 2, Order.Descending);进阶技巧:结合ZRANGEBYSCORE实现分段查询,比如查询2000-3000分的用户:
var midRange = await db.SortedSetRangeByScoreAsync( "leaderboard", 2000, 3000, Exclude.None, Order.Descending);3. List:实现操作日志队列
用户行为日志需要保证顺序且允许重复,List的LPUSH/RPOP组合是天然队列:
// 写入日志(左进右出) await db.ListLeftPushAsync("user:1001:logs", JsonConvert.SerializeObject(new { Action = "Login", Time = DateTime.UtcNow })); // 消费最新100条日志 var logs = await db.ListRangeAsync("user:1001:logs", 0, 99);注意:List适合轻量级队列,如需严格消息保证建议使用专用MQ
性能优化点:通过Pipeline批量插入日志:
var batch = db.CreateBatch(); for(int i=0; i<100; i++) { batch.ListLeftPushAsync("logs", $"log entry {i}"); } batch.Execute();4. Set:高效标签系统实现
用户标签需要去重和集合运算,Set提供O(1)时间复杂度的操作:
// 给用户打标签 await db.SetAddAsync("user:1001:tags", "科技"); await db.SetAddAsync("user:1001:tags", "数码"); // 检查是否包含某标签 bool hasTechTag = await db.SetContainsAsync("user:1001:tags", "科技"); // 计算共同兴趣(集合交集) var commonTags = await db.SetCombineAsync( SetOperation.Intersect, "user:1001:tags", "user:1002:tags");业务场景扩展:用SUNIONSTORE实现标签聚合:
// 合并多个用户的标签 await db.SetCombineAndStoreAsync( SetOperation.Union, "group:admins:tags", "user:1001:tags", "user:1002:tags");5. 数据结构选型决策树
面对业务需求时,参考以下决策路径:
| 需求特征 | 适用结构 | 示例场景 |
|---|---|---|
| 需要完整读写整个对象 | String | 小型配置项 |
| 需要独立读写对象属性 | Hash | 用户档案 |
| 需要维护有序集合 | SortedSet | 排行榜、优先级队列 |
| 需要保证顺序且允许重复 | List | 操作日志、时间线 |
| 需要去重和集合运算 | Set | 标签系统、好友关系 |
内存优化技巧:
- 当Hash字段超过100个时,考虑使用HSET的压缩列表优化
- 对大型SortedSet,定期执行
ZREMRANGEBYRANK清理尾部数据 - 监控List长度,避免单个Key过大影响集群均衡