C# 进程外缓存方法 C#如何使用Redis或Memcached

2次阅读

<p>选 redis 还是 memcached 取决于数据结构需求:Memcached 仅支持 String 且无持久化,Redis 支持多种数据类型及高级功能;C# 中推荐 StackExchange.Redis 连 Redis(全局单例复用 ConnectionMultiplexer),新项目勿用已停更的 Enyim.Caching 连 Memcached。</p>

C# 进程外缓存方法 C#如何使用Redis或Memcached

Redis 和 Memcached 在 C# 中的本质区别

选 Redis 还是 Memcached,不是看哪个“更流行”,而是看你要存什么。Memcached 只支持 string 类型的键值对,且不支持持久化、没有原生集合操作;Redis 支持 stringhashlistsetzset,还能做简单消息队列、带过期时间的分布式锁。如果你只需要高速缓存 json 字符串或二进制 blob,Memcached 足够轻量;但只要涉及对象结构、计数器、排行榜、延迟任务,Redis 是唯一合理选择。

用 StackExchange.Redis 连 Redis(推荐方式)

StackExchange.Redis 是目前 C# 生态最稳定、高性能、线程安全的 Redis 客户端。它默认使用连接池,不建议每次操作都 new ConnectionMultiplexer。常见错误是把 ConnectionMultiplexer 声明为局部变量,导致频繁重建连接、CPU 升高、出现 SocketFailureTimeoutException

正确做法:

  • 全局单例复用 ConnectionMultiplexer(用 Static readonly 或 DI 注册为 Singleton)
  • IDatabase 获取数据库实例(它是无状态的,可随时获取)
  • 避免在 async 方法里混用 .Wait().Result,会引发死锁

示例片段:

private static readonly Lazy<ConnectionMultiplexer> LazyConnection =     new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect("localhost:6379")); <p>public static ConnectionMultiplexer Connection => LazyConnection.Value;</p><div class="aritcle_card flexRow">                                                         <div class="artcardd flexRow">                                                                 <a class="aritcle_card_img" href="/ai/758" title="Nanonets"><img                                                                                 src="https://img.php.cn/upload/ai_manual/001/503/042/68b6db6adbfa5954.png" alt="Nanonets"  onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>                                                                 <div class="aritcle_card_info flexColumn">                                                                         <a href="/ai/758" title="Nanonets">Nanonets</a>                                                                         <p>基于AI的自学习OCR文档处理,自动捕获文档数据</p>                                                                 </div>                                                                 <a href="/ai/758" title="Nanonets" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>                                                         </div>                                                 </div><p>// 使用时 var db = Connection.GetDatabase(); await db.StringSetAsync("user:1001", JsonSerializer.Serialize(user), TimeSpan.FromMinutes(30));

用 Enyim.Caching 连 Memcached(仅限遗留项目)

Enyim.Caching 是 .NET Framework 时代主流的 Memcached 客户端,但已多年未更新,不支持 .NET 5+ 的 Span<byte> 优化,且配置复杂、日志不透明。新项目不要选它。如果必须维护老系统,请注意:

  • MemcachedClient 不是线程安全的,不能全局单例 —— 必须每个请求/作用域新建,或用 ObjectPool<MemcachedClient> 管理
  • 所有值都会被序列化为 byte[],默认用 BinaryFormatter(已废弃且不安全),务必替换为 System.Text.JsonNewtonsoft.Json 实现自定义 ITranscoder
  • Memcached 不支持 key 的命名空间或批量删除,清缓存只能靠 key 前缀 + 扫描(不推荐)或设短 TTL

缓存穿透、雪崩、击穿的 C# 应对思路

这些不是 Redis/Memcached 自带的功能,得靠代码层兜底。比如缓存穿透(查不到还反复打 DB),不能只靠“缓存空值”,还要加随机过期时间防集中失效:

  • 空结果也缓存,但 TTL 设为 TimeSpan.FromSeconds(60 + new Random().Next(30))
  • 热点 key 击穿:用 MemoryCache 做本地二级缓存(如 microsoft.Extensions.Caching.Memory),配合 GetOrCreateAsync + lockSemaphoreSlim 控制回源并发
  • 避免 Redis 雪崩:不同业务 key 的过期时间别对齐,可用 DateTimeOffset.UtcNow.AddSeconds(ttlSeconds + new Random().Next(120))

真正容易被忽略的是:Redis 连接超时和重试策略。默认 ConnectTimeout=5000ms,网络抖动时大量请求卡住。应在 ConfigurationOptions 中显式设置 ConnectRetry = 3ReconnectDelay = TimeSpan.FromMilliseconds(500),并监听 ConnectionFailed 事件做告警。

text=ZqhQzanResources