【缓存】缓存中常见的4种问题分析以及解决方案

      由于最近要准备换工作,同时最近在“Redis中国用户组”上关注了一系列Redis的活动,想要总结一下,Redis当做缓存使用过程中的一些常见问题。


一、前提

      1.文中相关术语

     (1)缓存命中:

       终端用户访问加速节点时,如果该节点有缓存住了要被访问的数据时就叫做命中,如果没有的话需要回原服务器取,就是没有命中。(百科)

     (2)过期时间:

       EXPIRE,是一个常用的Redis命令,允许用户为某个key指定超时时间,当超过这个时间之后key对应的值会被清除。延展到“缓存失效策略”,这部分内容之后详述。


      2.Redis中国用户组的CRUG活动

      http://www.itdks.com/eventlist/detail/1210 (北京)

      http://www.itdks.com/eventlist/detail/1299 (上海)

      http://www.itdks.com/eventlist/detail/1301 (深圳)

      阿里、新浪微博、唯品会、去哪儿、小米、饿了么、360等公司技术大牛讲解他们对Redis使用过程中问题的总结,强烈推荐上述内容,以及“IT大咖说”。


二、Redis常见问题

       我自己画了个图,参考如下:

     

        除了在“Redis中国用户组”里,阿里、新浪微博、饿了吗技术大牛所提出的“查找大Key”问题之外,常见的缓存问题无非如下三类:

        0.缓存一致性

        1.缓存并发

        2.缓存雪崩

        3.缓存击穿

       (描述顺序,和我的图逆序了,为了强调3个问题的递进关系)


       0)缓存一致性

       1.当数据时效性要求很高时,

       2.需要保证缓存中的数据与数据库中的保持一致,

       3.而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象。(集群同步)

       这就比较依赖缓存的过期和更新策略。一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存。针对缓存一致性,我在之前的博客中转载过一篇大牛写的更新策略:http://www.voidcn.com/article/p-dzjqrofm-bnw.html

       

       1)缓存并发

       1.缓存过期或者在更新

       2.同时有大量的并发请求该key

       描述:如果网站并发访问高,一个缓存如果失效(在上述条件下),可能出现多个进程同时直接获取DB数据,这时候会对DB造成很大的访问压力。

       问题:比如缓存过期,此时大量请求落到DB上,可能导致“雪崩”发生;如果缓存更新,对某个key有大量的并发请求,此时请求获得的结果可能是更新之前或者更新之后,从而会导致“缓存一致性”的问题出现。

       解决策略:

       由于“缓存并发”问题一般发生在查询期间,而且问题出在缓存更新时的高并发时刻,思路上,就可以在这个时候,对key加锁。(对缓存查询加锁,如果 KEY 不存在,就加锁,然后查 DB 后写入缓存,然后解锁;)    

       流程图参考:(图片参考:http://www.cnblogs.com/dinglang/p/6133501.html

       


       2)缓存雪崩

       原因:

       1.某些原因(根本原因高并发),缓存更新或者过期等导致缓存命中失效。

       2.大量请求直接落到DataBase上面,致其无法承受压力,进而崩溃。

       (是缓存并发的递进与升级版)

       解决方案:

       1.平时我们设定一个缓存的过期时间时,可能有一些会设置1分钟后或5分钟后。当并发很高时,会出在某一个时间同时生成了很多的缓存,并且过期时间都一样,这个时候就可能引发一当过期时间到后,这些缓存同时失效,请求全部转发到 DB ,DB 可能会压力过重,这样子,我们就可以将缓存过期时间均匀地分布在时间轴上,避免缓存同时失效、更新的情况发生。

       2.控制缓存并发、击穿现象的发生,查找大key,想解决方案。


       3)缓存击穿

       1.前提:某个key对应的数据为空(在缓存,db中)

       2.条件:该key被高并发访问,缓存访问没有命中,尝试去从后端数据库中获取,从而导致了大量请求达到数据库,而当该key对应的数据本身就是空的情况下,这就导致数据库中并发的去执行了很多不必要的查询操作,从而导致巨大冲击和压力。

       3.解决方案:

       1)通过设置过滤器(使用布隆过滤器),将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

       

        2)设置指定值

       如果一个查询返回的数据为空(可能由于数据不存在,也可能是系统故障),我们仍然把这个空结果进行缓存,缓存的值设定为一个指定值,同时设置它的过期时间很短,最长不超过五分钟。(因为缓存会占用内存,长时间缓存一个不存在的值比较耗资源。)在这五分钟内,这个值可能由于写入操作从而不再是一个不存在的值,这是就要更新缓存,用真实值替代指定值。
       比如,设定不存在的值缓存为 &&,也就是 "key" ===> "&&"。当返回这个 && 值的时候,就可以认为这是不存在的 key,然后决定是继续等待访问,还是放弃掉这次操作。如果继续等待访问,那么经过一个时间轮询点后,再次请求这个 key,如果取到的值不再是 &&,则可以认为这时候 key 有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中。


三、总结

      观察共同点:

      1.高并发

      2.时机:缓存更新、缓存失效居多


      比较:

      1.并发和雪崩

      “并发”和“雪崩”都是高并发时候缓存不被命中直接访问DB的例子,但是为什么还会把他们定义为两个常见的问题?我的理解,前者是因,或者是果,“并发”造成的影响还不够恶劣,雪崩会导致数据库直接崩溃。


      2.关联关系

      “并发”和“击穿”可能会导致“雪崩”发生,参考我的思维导图,避免发生“雪崩”,除了给出的解决方案,更要防止“并发”和“击穿”的发生啊。


      That's all.

相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
公众号推荐
   一个历史类的公众号,欢迎关注
一两拨千金