1 缓存穿透
缓存穿透指查询了数据库中不存在的数据,每次查询都会先查询一级、二级缓存 在查询到最末端的数据库。
缓存穿透的原因有业务逻辑本身的问题和恶意攻击导致。
- 针对业务逻辑的问题:可以约定在一定时间内对返回为空的key值进行缓存,使得在一定时间内缓存只被穿透一次。
- 针对恶意攻击 可以使用布隆过滤器来解决。所谓恶意攻击是指请求者刻意构造数据库中肯定不存在的 Key 值,然后发送大量请求进行查询。布隆过滤器是用最小的代价来判断某个元素是否存在于某个集合的办法。如果布隆过滤器给出的判定结果是请求的数据不存在,那就直接返回即可,连缓存都不必去查。虽然维护布隆过滤器本身需要一定的成本,但比起攻击造成的资源损耗仍然是值得的。
2 缓存击穿
缓存击穿指某些热点数据的缓存因为某种原因失效了,譬如缓存过期,此时有针对该数据的多个请求同时到达,这些请求全部去查数据库,导致数据库压力倍增。
解决方案有如下两种:
- 加锁同步,以请求该数据的key值为锁,使得只有第一个的请求去查真实数据源。其他线程阻塞或者重试策略。
- 热点数据由代码手动管理。缓存击穿是由与热点数据自动失效引起的,针对这种情况,可有开发者通过代码有计划的更新、失效 避免由缓存的策略自动管理。
3 缓存雪崩
缓存击穿指单个热点数据失效,缓存雪崩是指大批不同的数据在短时间内一起失效,这些数据的请求都会到达数据源,导致数据源在短时间内压力剧增。
原因是 系统具有专门的缓存预热功能,也有可能是大量的公共数据由某一次冷操作加载的,这都有可能导致缓存在同一时间过期;还有一种可能是缓存服务由于某种原因崩溃后重启导致的。
解决方案如下:
- 建立分布式缓存的集群。
- 启用多级透明缓存,各个服务节点的一级缓存通常具有不同的加载时间,也就分散了过期时间。
- 将缓存的生存期从一个固定的时间改成一个时间段的随机时间。
4 缓存污染
缓存污染指缓存的数据与真实数据源的数据不一致的情况。
缓存污染通常由开发者更新缓存不规范导致的,例如:从缓存中获取了某个对象,更新了对象的属性,后续由于业务发生异常数据进行了回滚,没有更新到数据库。导致数据库与缓存数据不一致情况。
更新缓存需遵守一定的设计模式,其中成本最低、最简单的是cache aside为:
- 读数据时,先读缓存,缓存没有在读数据源,然后更新到缓存,在响应请求。
- 写数据时,先写数据库,然后失效(而不是更新)缓存。
读数据方面一般没什么出错的余地,但是写数据时,就有必要专门强调两点:一是先后顺序是先数据源后缓存。试想一下,如果采用先失效缓存后写数据源的顺序,那一定存在一段时间缓存已经删除完毕,但数据源还未修改完成,此时新的查询请求到来,缓存未能命中,就会直接流到真实数据源中。这样请求读到的数据依然是旧数据,随后又重新回填到缓存中。当数据源的修改完成后,结果就成了数据在数据源中是新的,在缓存中是老的,两者就会有不一致的情况。另一点是应当失效缓存,而不是去尝试更新缓存,这很容易理解,如果去更新缓存,更新过程中数据源又被其他请求再次修改的话,缓存又要面临处理多次赋值的复杂时序问题。所以直接失效缓存,等下次用到该数据时自动回填,期间无论数据源中的值被改了多少次都不会造成任何影响。