缓存相关-先埋个坑后续填补

460 阅读4分钟

说到缓存,离不开一些概念:缓存击穿,缓存穿透,缓存雪崩。谈谈我个人对这些概念的理解吧。

  • 缓存穿透

请求没有访问到缓存,而是直接访问到了数据库层面,但是数据库层面也没有返回数据。
这种情况很多都是恶意攻击的时候出现的。

场景:有个商品详情的接口/goods/{id},一般id都是数据库自增的,从1开始,如/goods/1。假如有个/goods/-1的请求过来了, 由于数据库中不存在-1的商品信息,自然在缓存里也不会去缓存这样的数据,因此这样的请求就可能会直接访问到数据库,当有大批量的类似请求并发访问的时候,就有可能会给数据库造成压力,导致性能瓶颈。

解决办法

1. 参数校验。比如校验id是否合法,id < 1的直接过滤掉。

2. 降级方案是针对这些不合理的id做缓存,即,在数据库层访问结束后假如没有查到数据,则把这些id缓存下来,类似于做黑名单id一样,等下次再来访问的时候先检查id是否在黑名单id列表里,在的话就不要走后面的流程,直接返回。这种方案的缺点是只能拦截id相同的,假如一直用不同的id去访问的话,那黑名单id列表会不断的增大,导致缓存吃紧

  • 缓存击穿

请求访问过来的时候,刚好缓存处于失效状态,导致高并发的请求访问到了数据库层面,数据库有可能会因此而资源紧张。

场景:商品详情的请求 /goods/1 访问的时候,刚好 id=1 的缓存处于失效状态,导致该请求直接访问了数据库,数据库返回了 id=1数据 

解决办法

1. 不设置缓存失效时间,异步刷新缓存数据。即,当有请求过来的时候,检查缓存的有效状态,假如缓存状态是已失效,可以通过发送异步消息给后台,通过后台去刷新缓存数据。本次请求依旧返回缓存中的失效数据。

2. 可以设置缓存失效时间,但是通过加锁的方式刷新数据。即,当请求过来的时候,假如缓存中没有数据,则通过加锁的方式让请求排队,只允许一个请求去访问到数据库,防止并发访问,数据库返回数据后存到缓存当中,释放锁,第二个请求进来的时候判断是否缓存中已有数据,有的话直接读取。这种方案可能需要限制并发次数,避免大量请求排队导致web容器的连接池爆满,影响别的业务请求。

  • 缓存雪崩
指的是大量缓存在同一时刻失效的现象,导致各种请求访问的时候都没有读取到缓存的数据,而是全都访问了数据库。

场景:同时有商品数据、订单数据等缓存数据在同一瞬间失效,用户的访问请求全都直接访问到了数据库,数据库可能在瞬时承受着极高的并发访问。 

解决办法

1. 各种业务数据在设置缓存失效时间的时候,不要选择相同的失效时间,可以选择错峰失效的方式,比如选择夜深人静访问量少的时候失效,或者给失效时间再加个随机值,不要在整点时刻失效,比如选择在xx点xx分23秒的时候失效,避免缓存数据瞬间大量失效。

2. 缓存不要失效,异步刷新数据。

  • 总结

缓存穿透和缓存击穿有点类似,不同之处在于缓存穿透很多情况下是恶意访问导致的,而缓存击穿是内部缓存策略导致的。

缓存击穿和缓存雪崩都是内部缓存策略导致的,不同之处在于缓存击穿是某个点上的缓存问题,而缓存雪崩是一大片一大面的。点和面的区别。