如下是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://blog.csdn.net/zzh920625/article/details/78027534
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 有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中。
4)缓存颠簸问题
1.前提:缓存的颠簸问题,有些地方可能被成为“缓存抖动”,可以看做是一种比“雪崩”更轻微的故障,但是也会在一段时间内对系统造成冲击和性能影响。
2.条件:
一般是由于缓存节点故障导致。
3.解决方案:
业内推荐的做法是通过一致性Hash算法来解决。这里不做过多阐述,可以参照其他章节
5)缓存无底洞现象
1.前提
该问题由 facebook 的工作人员提出的, facebook 在 2010 年左右,memcached 节点就已经达3000 个,缓存数千 G 内容。
他们发现了一个问题---memcached 连接频率,效率下降了,于是加 memcached 节点,
添加了后,发现因为连接频率导致的问题,仍然存在,并没有好转,称之为”无底洞现象”。
2.前提:
目前主流的数据库、缓存、Nosql、搜索中间件等技术栈中,都支持“分片”技术,来满足“高性能、高并发、高可用、可扩展”等要求。有些是在client端通过Hash取模(或一致性Hash)将值映射到不同的实例上,有些是在client端通过范围取值的方式映射的。当然,也有些是在服务端进行的。但是,每一次操作都可能需要和不同节点进行网络通信来完成,实例节点越多,则开销会越大,对性能影响就越大。
3.解决方案:
主要可以从如下几个方面避免和优化:
数据分布方式:有些业务数据可能适合Hash分布,而有些业务适合采用范围分布,这样能够从一定程度避免网络IO的开销。
IO优化:可以充分利用连接池,NIO等技术来尽可能降低连接开销,增强并发连接能力。
数据访问方式:一次性获取大的数据集,会比分多次去获取小数据集的网络IO开销更小。
当然,缓存无底洞现象并不常见。在绝大多数的公司里可能根本不会遇到。
三、总结
观察共同点:
1.高并发
2.时机:缓存更新、缓存失效居多
比较:
1.并发和雪崩
“并发”和“雪崩”都是高并发时候缓存不被命中直接访问DB的例子,但是为什么还会把他们定义为两个常见的问题?我的理解,前者是因,或者是果,“并发”造成的影响还不够恶劣,雪崩会导致数据库直接崩溃。
2.关联关系
“并发”和“击穿”可能会导致“雪崩”发生,参考我的思维导图,避免发生“雪崩”,除了给出的解决方案,更要防止“并发”和“击穿”的发生啊。