Skip to content
bitzorcas
中EN

Reference

多级缓存与防击穿:FusionCache L1/L2 实战

深度剖析 BitzOrcas.Modern 多级缓存体系,揭秘 FusionCache 内存 L1 + Redis L2 双层架构、CacheScope 租户隔离、负缓存防御与提交后精准标签失效。

Last updated

在面向高并发的企业级应用中,不合理的缓存设计往往是系统雪崩的催化剂:

  • 直连 Redis 导致网络瓶颈:每次读操作都跨网络发起 Redis 调用,高 QPS 下网卡打满、连接池枯竭;
  • 缓存击穿与雪崩(Cache Stampede):热点 Key 一旦过期,成千上万个并发请求同时打向数据库,瞬间引发数据库 CPU 100% 锁死;
  • 跨租户脏读(Data Leakage):Key 命名随意,没有强类型租户与环境前缀,导致 A 租户读到了 B 租户的敏感数据!

BitzOrcas.Modern 采用基于 FusionCache 的 L1(进程内内存)+ L2(分布式 Redis)多级缓存架构,并通过强类型 ICacheKeyBuilder 与 ICacheStore 为全平台提供了工业级的缓存保护。

多级缓存读写与失效全景

优先微秒级读取L1 未命中L2 未命中写回数据并广播

1. 业务用例 (Handler)

2. ICacheKeyBuilder (自动注入租户/环境)

3. ICacheStore (统一访问入口)

4. FusionCache L1 (进程内内存缓存)

5. Redis L2 (分布式共享缓存)

6. 回源查询真实数据库 (自动单飞防击穿)

7. Redis Backplane 跨实例同步失效


第一步:使用 ICacheKeyBuilder 构造防越权缓存键

BitzOrcas 严禁手写拼接 Redis 字符串 Key。所有 Key 必须通过 ICacheKeyBuilder 构造,框架会自动追加 {app}:{env}:v{version}:{area}:{scope}:{parts}:

构造租户隔离的缓存 Key
using BitzOrcas.Application.Abstractions.Caching;
public sealed class CatalogOfferingCacheService(
ICacheKeyBuilder cacheKeys,
ICacheStore cache)
{
public async Task<OfferingView?> GetOfferingAsync(string offeringId, CancellationToken ct)
{
// ① 显式指定租户作用域,框架自动从 ICurrentUserAccessor 注入 TenantId
var key = cacheKeys.Build(
area: "catalog-offering",
scope: CacheScope.Tenant,
version: 1,
offeringId);
// ② 配置缓存策略:10 分钟正常过期,20 秒负缓存,启用 10% 随机抖动防雪崩
var policy = new CachePolicy(TimeSpan.FromMinutes(10))
{
NegativeTtl = TimeSpan.FromSeconds(20),
JitterRatio = 0.10,
AreaTag = "catalog",
Tags = [$"offering:{offeringId}"]
};
// ③ 读穿模式:自动执行单飞回源(Single-Flight),拦截并发击穿
var result = await cache.GetOrCreateAsync(
key,
async token => await LoadFromDbAsync(offeringId, token),
policy,
ct);
return result.Value;
}
private Task<OfferingView?> LoadFromDbAsync(string id, CancellationToken ct) => Task.FromResult<OfferingView?>(null);
}

第二步:三态读取与负缓存(Negative Caching)防御

针对恶意扫描或不存在的资源查询,如果数据库返回 null 就放弃缓存,会导致高频穿透。BitzOrcas 支持标准的三态结果(Hit / Negative / Miss):

三态缓存读取控制流
var cached = await cache.TryGetAsync<OfferingView>(key, cancellationToken);
if (cached.HasValue)
{
// 1. 正常命中有效数据
return Result<OfferingView>.Success(cached.Value!);
}
if (cached.IsNegative)
{
// 2. 命中负缓存(该资源已被确认为不存在,直接短路返回 404)
return Result<OfferingView>.Failure(OfferingErrors.NotFound);
}
// 3. 真正未命中(Miss)时才允许回源数据库
return await LoadFromDbAndPopulateAsync(offeringId, cancellationToken);

第三步:事务提交后精准标签失效(Tag Invalidation)

当商品数据发生变更时,严禁在事务未提交前清空缓存(否则事务若回滚,缓存已被污染)。标准模式是:事务成功提交 -> 调用 RemoveByTagAsync 广播失效:

UpdateOfferingCommandHandler.cs: 提交后失效
public async ValueTask<Result> Handle(UpdateOfferingCommand command, CancellationToken ct)
{
// 1. 保存聚合变更(TransactionPipeline 自动提交事务)
var saveResult = await repository.SaveAsync(offering, ct);
if (saveResult.IsFailure) return saveResult;
// 2. 事务成功后,按 Tag 批量广播失效当前商品的所有多维度缓存
await cache.RemoveByTagAsync($"offering:{command.Id}", ct);
return Result.Success();
}

总结

BitzOrcas 的多级缓存机制实现了极速与可靠的平衡:

  • L1 内存零延迟:高频数据纳秒级直出;
  • 并发单飞锁:彻底杜绝热点 Key 击穿数据库;
  • 严格租户隔离:强类型 KeyBuilder 防止跨租户数据串流。

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%