1. 引言:当字符串遇上Redis

在日常开发中,我们经常需要处理简单的键值对存储。比如用户登录状态、临时验证码、页面缓存等场景,使用Redis的字符串类型存储就像在手机通讯录里存电话号码一样自然。本文将以"手把手"的方式,带你用C#和StackExchange.Redis实现这些常见需求。


2. 环境准备:搭建造纸厂

在开始搬砖之前,我们需要准备以下工具:

  1. Visual Studio 2022+
  2. Redis服务器(本地或远程)
  3. NuGet包:StackExchange.Redis(当前版本2.6.66)
// 安装NuGet包命令
Install-Package StackExchange.Redis -Version 2.6.66

3. 建立连接:打开Redis大门

连接Redis就像用钥匙开锁,正确的配置是成功的关键。这里推荐使用连接复用机制,避免频繁创建连接导致的性能损耗。

using StackExchange.Redis;

// 创建连接复用器(推荐单例模式)
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379,password=yourpassword");

// 获取数据库对象(默认使用0号数据库)
IDatabase db = redis.GetDatabase();

/* 注释说明:
   1. Connect方法支持多种连接格式:
      - 单节点:127.0.0.1:6379
      - 集群:host1:port1,host2:port2
   2. GetDatabase()参数:
      - db:数据库编号(0-15)
      - asyncTimeout:异步超时(默认5000ms)
*/

4. 存储字符串:把大象放进冰箱

4.1 基本存储操作

// 简单设置(没有过期时间)
bool setResult = db.StringSet("user:1001:name", "张三");

// 带过期时间的设置(30分钟)
TimeSpan expiry = TimeSpan.FromMinutes(30);
setResult = db.StringSet("captcha:13800138000", "A7G9", expiry);

/* 注释说明:
   1. StringSet返回bool表示是否设置成功
   2. Redis键命名建议使用冒号分层:
     user:{id}:field
   3. 过期时间适用于临时数据存储
*/

4.2 高级存储技巧

// 条件设置(仅当键不存在时)
var setCondition = When.NotExists;
setResult = db.StringSet("config:site_maintenance", "false", expiry: null, setCondition);

// 批量设置(原子操作)
var keyValuePairs = new[] {
    new KeyValuePair<RedisKey, RedisValue>("weather:bj", "晴"),
    new KeyValuePair<RedisKey, RedisValue>("weather:sh", "多云")
};
db.StringSet(keyValuePairs);

/* 注释说明:
   1. When.NotExists类似数据库的INSERT
   2. 批量操作减少网络往返次数
   3. 所有操作默认支持异步方法(StringSetAsync)
*/

5. 读取数据:打开记忆宝盒

5.1 基础读取

// 简单读取
string userName = db.StringGet("user:1001:name");

// 处理空值(推荐方式)
RedisValue value = db.StringGet("non_existing_key");
if (!value.IsNull) {
    // 处理有效数据
}

/* 注释说明:
   1. StringGet返回RedisValue类型
   2. 直接转换可能引发InvalidCastException
   3. 使用IsNull判断比try-catch更高效
*/

5.2 批量读取与转换

// 批量获取(减少网络消耗)
RedisKey[] keys = { "weather:bj", "weather:sh" };
RedisValue[] values = db.StringGet(keys);

// 类型安全转换
if (db.StringGet("config:retry_count").TryParse(out int retryCount)) {
    // 使用数值类型操作
}

/* 注释说明:
   1. TryParse支持基本数据类型转换
   2. 批量读取顺序与输入数组顺序一致
   3. 缺失的键对应位置返回RedisValue.Null
*/

6. 应用场景:实战演练场

6.1 会话存储(Session Storage)

// 存储用户会话
string sessionId = Guid.NewGuid().ToString();
db.StringSet($"session:{sessionId}", JsonConvert.SerializeObject(userData), 
            TimeSpan.FromHours(2));

// 读取会话
var sessionData = JsonConvert.DeserializeObject<UserSession>(
    db.StringGet($"session:{sessionId}"));

6.2 API限流器

// 简易请求计数器
string apiKey = "API:13800138000";
long requestCount = db.StringIncrement(apiKey);
if (requestCount == 1) {
    db.KeyExpire(apiKey, TimeSpan.FromMinutes(1));
}

/* 注释说明:
   StringIncrement是原子操作
   适用于分布式环境下的计数场景
*/

7. 技术优缺点分析

7.1 优势亮点

  • 闪电速度:内存操作,读取速度可达10万+/秒
  • 简单直观:类似Dictionary的使用方式
  • 原子操作:支持INCR/DECR等线程安全操作
  • 灵活过期:支持精确到毫秒的TTL设置

7.2 局限注意

  • 值大小限制:单个Value最大512MB(实际建议<1MB)
  • 无结构存储:复杂数据需要自行序列化
  • 持久化风险:RDB快照可能丢失最新数据

8. 注意事项:避坑指南

  1. 连接泄漏:务必复用ConnectionMultiplexer实例
  2. 序列化陷阱:二进制数据需Base64编码
  3. 超时设置:合理配置SyncTimeout(默认5秒)
  4. 键命名规范:避免使用特殊字符(如空格、换行符)
  5. 内存监控:定期检查内存使用量,防止OOM

9. 总结:选择的艺术

通过StackExchange.Redis操作Redis字符串就像使用瑞士军刀处理日常任务——简单但功能强大。对于需要快速存取、临时存储或计数器的场景,字符串类型是最佳选择。但对于需要复杂查询或关系型数据,建议结合其他数据结构(如Hash、SortedSet)使用。