缓存失效时防止穿透DB的策略

1 篇文章 0 订阅
1 篇文章 0 订阅

下面以查询业务中缓存使用为例来说明:


1 我们平时写程序时一般按照如下流程来使用缓存



根据上面的流程,当缓存中没有数据A或A失效时,如果在高峰期,可能会出现所有的压力压到DB,导致DB变慢,从而导致所有涉及到db的业务变慢,甚至引起服务挂掉。

2 所以需要一个类似全局的锁,当一个客户端或线程查询缓存时,缓存中没有数据,其他客户端都等着之前的客户端从数据库中查询数据,改进的流程如下:



改进的地方使用了redis,其思想在于先去设置一个kv对,如果该kv对已经存在,则说明有线程或别的客户端已经去访问db了,只要等一会再从缓存取数据即可。

当然,可以不用redis,思想是相通的。

但是这个流程仍有一个问题,那就是只有一个客户端或线程去访问db,但是如果这个客户端挂掉,其余的客户端都在那等着,会陷入死循环。


3  所以需要这个全局的锁有个超时时间,超时自动删除,防止创建它的客户端死掉,改进的流程如下:


客户端在成功设置了kv对后,再对key做一个过期设置,这样,即使客户端由于某种原因挂掉了,key到期会被自动删除,其他客户端又可以重现进行设置了。

但是还有一个问题,假设客户端在对key设置过期时间时挂掉了,又会陷入第二中方案描述的情况。


4 所以需要在设置锁时加上过期时间,即设置锁和设置超时时间是个原子操作,要么成功,要么失败。但是redis的setNx不支持同时设置过期时间,所以只能将过期时间放到value中,而过期则需要其他客户端在获取不到锁时执行检查任务,看看该key是否过期了,过期就把key删除:



不幸的是,即使如上策略,也会发生问题,问题比较复杂,我描述如下:

1 A客户端设置kv获得锁: 123->1389606178281,5 表示key为123的键值对,在时间为1389606178281时设置,5秒后过期

2 B客户端设置kv,发现已经存在,故获取key为123的value,检查是否过期。

3 C客户端设置kv,发现已经存在,故获取key为123的value,检查是否过期。


假设B和C客户端同时执行检查,而此时如果恰巧A客户端超时了,那么B或C会将key为123的键值对删除。

我们假设B执行的快,那么key会被B删除,那么其他客户端此时会再次创建kv。

问题在这出现了,假设其他客户端执行的快,又创建了kv,而此时C由于检查的是A客户端那时的kv,即应该属于脏数据,所以C又执行了删除。

所以可能照样会有不止一个的客户端进行DB操作。


当然了,这种情况已经属于比较极端的情况,已经能够防止大量客户端访问DB了,但是还不够完美,原因就是出在判断超时是用的是脏数据。

如果想解决这种情况,我们需要使用redis的一个特性getset key value, 即将key设置为value,并返回旧的value,这个操作是原子的。


5 改进的流程如下:


改造后,即使多个线程或客户端执行getset,后执行的获取返回的值由于是新的客户端设置进去的,所以不会出现超时,就不存在多个客户端操作db了


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值