zoukankan      html  css  js  c++  java
  • 应用缓存的常见问题及解决

    使用缓存一些常见的套路问题。

     

    缓存穿透

    • 场景:大量请求访问某个不存在的KEY

    在缓存设计中,查询缓存 -> key不存在 -> 回源DB -> 更新缓存,这是一个典型的方案。

    缓存穿透是指查询一个一定不存在的Key,由于缓存层不存在,将导致这个不存在的数据每次请求都要到存储层去查询,直接对DB造成影响。在恶意攻击和失败回调中可能会出现这种情况。

    • 解决方案

    1.对空对象进行缓存。对查询结果为空的情况也进行缓存,如当此查询结果为空,设置Key对应对象为NULL,缓存时间设置短一点,存储层中有数据后及时更新。

    2.对所有可能查询的参数Key以hash形式存储,在控制层先进行校验,不符合则丢弃。最常见的是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。比较适合命中不高,但是更新不频繁的数据。

     

    缓存失效

    • 场景:缓存中大量的Key集中在一段时间内失效,数据库的压力凸显
    • 解决方案

    1.可以分析用户行为,尽量让失效时间点均匀分布。针对失效时间相同的key,在设置失效时间时不设置固定的时间,而在原有基础上加上一个随机的值,比如1分钟-5分钟,这样就可以有效分散开缓存失效的时间。

    2.考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

     

    缓存并发

    • 场景:在高并发场景下,某些业务有可能多个请求并发的去从数据库获取数据

    有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大。

    • 解决方案

    1.添加分布式锁,在缓存更新或者过期的情况下,先尝试获取到锁,当更新或者从数据库获取完成后再释放锁,其他的请求只需要牺牲一定的等待时间,即可直接从缓存中继续获取数据。

    2.定期从DB里查询数据,再刷到缓存里面,确保缓存里面的数据一直可以读到。

     

    缓存雪崩

    • 场景:当发生大量的缓存穿透,例如缓存挂掉,或者对某个失效的缓存的大并发访问

    由于缓存扛了大量的请求,有效保护了数据库的安全。但是当缓存雪崩,所用请求就会瞬间全部打到DB上,可能会导致数据库崩溃。

    • 解决方案

    1.保证缓存服务的高可用性,当一个实例挂掉的时候,请求也可以转移到集群的其他实例上。缓存失效时的雪崩效应对底层系统的冲击非常大,这时候可以使用双缓存机制,在工作缓存之外另外维护一层灾备缓存。

    2.使用降级策略,当缓存服务出现问题时,可以暂时对用户展示一份固定的数据,避免系统的崩溃,等待缓存服务的恢复。前端也应该有此机制,比如当后端接口返回非正常数据时,将之前保存的旧数据固定展示给用户,避免页面崩溃的问题。

     

    缓存数据的淘汰

     

    缓存淘汰的策略有两种:

    1.定时去清理过期的缓存

    2.当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存

    两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,根据自己的应用场景来权衡。

     

    更新缓存还是淘汰缓存

     

    什么是更新缓存:数据不但写入数据库,还会写入缓存

    什么是淘汰缓存:数据只会写入数据库,不会写入缓存,只会把数据淘汰掉

    更新缓存的优点:缓存不会增加一次miss,命中率高

    淘汰缓存的优点:简单

     

    先操作数据库还是先操作缓存

     

     

    假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现DB中是新数据,Cache中是旧数据,数据不一致。

    假设先淘汰缓存,再写数据库:第一步淘汰缓存成功,第二步写数据库失败,则只会引发一次Cache miss。

    所以结论是:先淘汰缓存,再写数据库

     

    参考:

    缓存与数据库一致性保证

    缓存更新的套路

  • 相关阅读:
    Leetcode Binary Tree Preorder Traversal
    Leetcode Minimum Depth of Binary Tree
    Leetcode 148. Sort List
    Leetcode 61. Rotate List
    Leetcode 86. Partition List
    Leetcode 21. Merge Two Sorted Lists
    Leetcode 143. Reorder List
    J2EE项目应用开发过程中的易错点
    JNDI初认识
    奔腾的代码
  • 原文地址:https://www.cnblogs.com/binyue/p/8057890.html
Copyright © 2011-2022 走看看