.NET CORE WEB API 缓存2

前言

缓存数据后,数据库中的数据可能会修改,导致缓存的是旧的数据,怎么来获取数据库中最新的数据呢?

如果没有进行设置的话,缓存不会过期,除非重启服务器。(代价太大了)

解决方法

  1. 在数据改变的时候调用Remove或者Set来删除或者修改缓存(优点:及时。缺点:比较麻烦);
  2. 过期时间(只要过期时间比较短,缓存数据不一致的情况也不会持续很长时间)

有两种过期时间策略:绝对过期时间、滑动过期时间。它们分别是什么?

缓存的绝对过期时间

从缓存保存后,多长时间缓存有效,超过时间,缓存失效

1、GetOrCreaeteAsync()方法的回调方法中有一个ICacheEntry类型的参数,通过ICacheEntry对当前的缓存项做设置

2、AbsoluteExpirationRelativeToNow用来设定缓存项的绝对过期时间

缓存的滑动过期时间

只要在缓存没过期的时候请求一次,缓存自动续命一段时间

使用ICacheEntry的SlidingExpiration属性用来设定缓存项的滑动过期时间

两种过期时间混用

使用滑动过期时间策略,如果一个缓存项一直被频繁访问,那么这个缓存项就会出现一直被续期而不过期的问题。解决:可以对一个缓存项同时设定滑动过期时间和绝对过期时间,并且把绝对过期时间设定的比滑动过期时间长,这样缓存项的内容会在绝对过期时间内随着访问被滑动续期,但是一旦超过了绝对过期时间,缓存项就会被删除。

到底采用哪种策略?

1、无论用哪种过期时间策略,程序中都会存在缓存数据不一致的情况。部分系统(博客等)无所谓,部分系统不能忍受(比如金融),取决你的业务场景。对数据要求严格的就不能使用过期时间这种策略了,其他情况大部分时间绝对过期时间就可以了,偶尔会使用混合策略

2、可以通过其他机制获取数据源改变的信息,再通过代码调用IMemoryCache的Set方法更新缓存

缓存穿透问题

什么是缓存穿透

传入的Id如果不存在,就会导致每一次请求的返回值都是null,数据库和缓存里面都没有数据,恶意用户可能反复调用不存在的数据,每一次都会执行数据库查询操作。从而给数据库造成极大的压力。

string cacheKey = "Book" + id;//缓存键
Book? b = memCache.Get<Book?>(cacheKey);
if(b==null)//如果缓存中没有数据
{
    //查询数据库,然后写入缓存
    b = await dbCtx.Books.FindAsync(id);
    memCache.Set(cacheKey, b);
}


缓存穿透的解决方案

1、解决方法:把"查不到"也当成一个数据放入缓存

2、我们用GetOrCreateAsync方法即可,因为它会把null值也当成合法的缓存值

string cacheKey = "Book" + id;
var book = await memCache.GetOrCreateAsync(cacheKey, async (e) => {
    var b = await dbCtx.Books.FindAsync(id);
    logger.LogInformation("数据库查询:{0}",b==null?"为空":"不为空");
    return b;
});
logger.LogInformation("Demo5执行结束:{0}", book == null ? "为空" : "不为空");

建议使用GetOrCreateAsync方法,虽然有点麻烦,但用起来更安全

缓存雪崩和数据混乱问题

缓存雪崩

1、缓存项集中过期引起缓存雪崩

2、解决方法:再基础过期时间之上,再加一个随机的过期时间

 缓存数据混乱

public User GetUserInfo()
{
    Guid userId=...;//获取当前用户Id
    return memCache.GetOrCreate("UserInfo", (e) => {
        return ctx.User.Find(userId);
    });
}

解决方法:合理设置key

封装内存缓存操作的帮助类

需求
1、IQueryable、IEnumerable等类型可能存在着延迟加载的问题,如果把这两种类型的变量指向的对象保存到缓存中,在我们把它们取出来再去执行的时候,如果它们延迟加载时候需要的对象已经被释放的话,就会执行失败。因此缓存禁止这两种类型

2、实现随机缓存过期时间

接口

public interface IMemoryCacheHelper
{
    TResult? GetOrCreate<TResult>(string cacheKey, 
    Func<ICacheEntry, TResult?> valueFactory, int expireSeconds);
    Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, 
    Func<ICacheEntry, Task< TResult?>> valueFactory, int expireSeconds);
    void Remove(string cacheKey);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值