C#与Redis实战:基于StackExchange.Redis的数据操作全解析
1. 环境准备与基础配置
第一次接触Redis时,我被它惊人的读写性能震撼到了。作为C#开发者,选择StackExchange.Redis这个开源客户端绝对是明智之举。记得当时为了测试性能,我用本地Docker快速搭建了一个Redis实例,整个过程不到5分钟。如果你还没安装Redis,推荐直接通过Docker运行:
docker run --name myredis -p 6379:6379 -d redis在Visual Studio中新建控制台项目后,通过NuGet添加StackExchange.Redis只需两步:
- 右键项目选择"管理NuGet程序包"
- 搜索并安装
StackExchange.Redis(当前稳定版是2.6.66)
连接字符串的配置有个小技巧:我习惯用ConfigurationOptions对象而不是纯字符串,因为可以更灵活地设置重试策略、超时等参数。这是我常用的初始化代码:
var config = new ConfigurationOptions { EndPoints = { "localhost:6379" }, ConnectTimeout = 5000, // 5秒连接超时 SyncTimeout = 10000, // 10秒操作超时 AbortOnConnectFail = false // 自动重连 }; ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(config);2. 字符串操作实战
字符串是Redis最基础的数据类型,但千万别小看它。去年我们项目就用字符串缓存实现了电商秒杀系统,QPS轻松突破10万。来看几个实用场景:
场景1:带过期时间的缓存
IDatabase db = redis.GetDatabase(); // 缓存用户会话,30分钟自动过期 db.StringSet("user:1001:session", "session_token", TimeSpan.FromMinutes(30)); // 原子性计数器 db.StringIncrement("product:2002:views"); // 阅读量+1场景2:对象序列化存储配合Json.NET可以优雅地存储复杂对象:
var user = new { Id = 1001, Name = "张三", LastLogin = DateTime.Now }; db.StringSet("user:1001", JsonConvert.SerializeObject(user)); // 反序列化读取 var userData = JsonConvert.DeserializeObject(db.StringGet("user:1001"));踩坑提醒:StringSet默认是覆盖写入,如果需要"不存在时才设置"的原子操作,要加上条件:
db.StringSet("lock:order", "locked", when: When.NotExists);3. 哈希表高级用法
哈希特别适合存储对象属性,我们用户系统的个人资料就全用Hash存储。对比字符串存储整个对象,Hash可以精准修改单个字段:
// 存储用户属性 db.HashSet("user:1001", new HashEntry[] { new HashEntry("name", "李四"), new HashEntry("age", 28), new HashEntry("vip", true) }); // 只获取部分字段 var userName = db.HashGet("user:1001", "name"); // 原子性字段自增 db.HashIncrement("user:1001", "login_count");有个性能优化技巧:批量操作时使用HashGetAll比多次HashGet更高效。我们做过测试,获取10个字段时批量操作能快3-5倍。
4. 列表与集合实战
列表实现消息队列我们日志系统就用Redis列表做缓冲队列:
// 生产者 db.ListRightPush("log:queue", "error: connection timeout"); // 消费者 while(true) { var log = db.ListLeftPop("log:queue"); if (!log.IsNull) ProcessLog(log); else Thread.Sleep(100); }集合去重应用处理用户标签时,集合自动去重的特性太有用了:
// 添加用户标签 db.SetAdd("user:1001:tags", "电子产品"); db.SetAdd("user:1001:tags", "数码爱好者"); // 获取共同兴趣 db.SetIntersect("user:1001:tags", "user:1002:tags");5. 有序集合深度应用
有序集合在我们排行榜功能中表现惊艳。比如实现直播打赏榜:
// 更新主播热度值 db.SortedSetIncrement("live:ranking", "主播A", 500); // 获取TOP10 var top10 = db.SortedSetRangeByRank("live:ranking", 0, 9, Order.Descending); // 带分数返回 var topWithScores = db.SortedSetRangeByRankWithScores("live:ranking", 0, 4, Order.Descending);有个实用技巧:使用SortedSetCombine可以合并多个榜单,我们用它实现了跨服游戏排行榜。
6. 键管理与生产实践
键命名规范经过多个项目总结,推荐这样的命名规则:
业务:子业务:ID[:属性] 例如: order:20230815:10086:status user:1001:profile批量操作优化使用管道(Pipeline)能大幅提升批量操作性能:
var batch = db.CreateBatch(); batch.StringSetAsync("key1", "value1"); batch.StringSetAsync("key2", "value2"); batch.Execute();连接复用技巧千万别每次操作都创建新连接!最佳实践是全局维护一个ConnectionMultiplexer实例。我们在ASP.NET Core中通常这样注册:
services.AddSingleton<IConnectionMultiplexer>( ConnectionMultiplexer.Connect(configuration));7. 性能调优经验
在日均亿级请求的系统中,我们总结出这些黄金法则:
- 合理设置过期时间:即使是持久化数据也建议设置TTL,防止冷数据堆积
- 控制Value大小:单个Value不要超过10KB,大Value考虑分片存储
- 慎用KEYS命令:生产环境绝对禁用KEYS *,用SCAN替代
- 监控慢查询:定期检查slowlog发现性能瓶颈
// 获取慢查询日志 var slowLog = redis.GetServer("localhost", 6379).SlowlogGet();8. 异常处理与调试
Redis操作必须做好错误处理,特别是网络波动时的重试机制。这是我们封装的安全操作模板:
try { var tran = db.CreateTransaction(); tran.AddCondition(Condition.StringEqual("lock:order", "true")); tran.StringSetAsync("order:1001", "paid"); bool committed = tran.Execute(); } catch(RedisConnectionException ex) { // 处理网络异常 logger.Error("Redis连接失败", ex); } catch(RedisTimeoutException ex) { // 处理超时 logger.Warn("操作超时", ex); }调试时推荐使用RedisInsight工具,它能直观展示数据结构,比命令行友好多了。
