【Redis笔记】缓存穿透、缓存雪崩、缓存击穿解决方案
缓存穿透
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
解决方案
缓存空对象
布隆过滤
增强id的复杂度,避免被猜测id规律
做好数据的基础格式校验
加强用户权限校验
做好热点参数的限流
缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案
给不同的Key的TTL添加随机值
利用Redis集群提高服务的可用性
给缓存业务添加降级限流策略
给业务添加多级缓存
缓存击穿
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
解决方案
- 互斥锁
性能受影响,线程需要等待,实现简单,保证一致性
public class WaitToFinishMemoryCache<TItem>
{
private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
private ConcurrentDictionary<object, SemaphoreSlim> _locks = new ConcurrentDictionary<object, SemaphoreSlim>();
public async Task<TItem> GetOrCreate(object key, Func<Task<TItem>> createItem)
{
TItem cacheEntry;
if (!_cache.TryGetValue(key, out cacheEntry))// Look for cache key.
{
SemaphoreSlim mylock = _locks.GetOrAdd(key, k => new SemaphoreSlim(1, 1));
await mylock.WaitAsync();
try
{
if (!_cache.TryGetValue(key, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = await createItem();
_cache.Set(key, cacheEntry);
}
}
finally
{
mylock.Release();
}
}
return cacheEntry;
}
}
var _avatarCache = new WaitToFinishMemoryCache<byte[]>();
var myAvatar = await _avatarCache.GetOrCreate(userId, async () => await _database.GetAvatar(userId));
- 逻辑过期
性能好,线程无需等待,实现复杂
缓存数据时添加一个逻辑过期时间,并不是真正的过期时间
实现逻辑:
定义一个缓存对象用于附加逻辑过期时间,如:
public class CacheData<T>{
public T Data{get;set;}
public DateTime ExpireTime{get;set;}
}