概述
读取缓存比读取数据库实在是快的太多了,所以在软件系统开发中,缓存通常是用来加快数据访问的非常有效的手段。
实现方案
下面是高性能并发的实现方案的精简版代码:
public async Task<string> GetDataAsync(string key)
{
// 1️⃣ 缓存命中 → 直接返回(90%+的请求走这里)
var cached = await _cache.GetAsync(key);
if (cached != null) return cached;
// 2️⃣ 布隆过滤器 → 拦截不存在的key
if (!_bloomFilter.MightContain(key)) return null;
// 3️⃣ Per-Key锁 → 只锁当前key
var semaphore = _keyLocks.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));
await semaphore.WaitAsync();
try
{
// 4️⃣ Double-Check → 避免重复查询
cached = await _cache.GetAsync(key);
if (cached != null) return cached;
// 5️⃣ 查询数据库
var data = await _database.QueryAsync(key);
// 6️⃣ 写缓存(含空值保护)
await _cache.SetAsync(
key,
data ?? "nil",
data != null ? TimeSpan.FromMinutes(30) : TimeSpan.FromMinutes(5)
);
return data;
}
finally
{
semaphore.Release();
}
}
总结
这个方案是单体应用方案,它的核心思想是使用细粒度的 per-key 锁,并通过布隆过滤器前置过滤,实现高并发缓存读写,如果面对的是分布式应用,则又复杂得多了。
• Per-key 锁、双重检查,只有相同的 key 才会互斥,不同的 key 可以并发执行,同时避免热点数据过期瞬间的缓存击穿
• 布隆过滤器、缓存空值(nil),应对查询不存在的数据,防止缓存穿透
• 可扩展使用随机TTL,避免大量key同时过期,导致缓存雪崩
最后,如果本文对你有帮助,不妨👍,谢谢!