文章目录

  • 1. 缓存雪崩
  • 2. 缓存穿透
  • 3. 缓存热点
  • 4. 大key问题
  • 4.1 如何优化bigkey
  • 5. 缓存数据一致性
  • 6. 参考


1. 缓存雪崩

缓存雪崩是指,在某一个时间段,缓存集中过期失效.

很多业务场景,如:秒杀商品、微博热搜排行、或者一些活动数据,都是通过跑任务方式,将DB数据批量、集中预热到缓存中,缓存数据有着近乎相同的过期时间。

当过这批数据过期时,会一起过期,此时,对这批数据的所有请求,都会出现缓存失效,从而将压力转嫁到DB,DB的请求量激增,压力变大,响应开始变慢。

那么有没有解呢?

当然有了。

解决方案一:

发现缓存不存在,在读取数据库时,再保存到redis缓存中,采用队列的方式或加锁的方式,进行排队访问数据库,这样就能够避免同时打到数据库上。

解决方案二:

我们可以从缓存的过期时间入口,将原来的固定过期时间,调整为过期时间=基础时间+随机时间,让缓存慢慢过期,避免瞬间全部过期,对DB产生过大压力。

2. 缓存穿透

不是所有的请求都能查到数据,不论是从缓存中还是DB中。缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会命中。

不管使用哪种缓存系统都有可能遇到缓存穿透的问题,少量的缓存穿透对系统也没有损害,但是如果你的系统遭遇攻击,存在大量的缓存穿透的话,那么可能就是一个麻烦了,如果大量的缓存穿透超过了后端服务器的承受能力,那么就有可能造成服务崩溃,这是不可接受的。

假如黑客攻击了一个论坛,用了一堆肉鸡访问一个不存的帖子id。按照常规思路,每次都会先查缓存,缓存中没有,接着又查DB,同样也没有,此时不会预热到Cache中,导致每次查询,都会cache miss。

由于DB的吞吐性能较差,会严重影响系统的性能,甚至影响正常用户的访问。

解决方案:

  • 方案一:查存DB 时,如果数据不存在,预热一个特殊空值到缓存中。这样,后续查询都会命中缓存,但是要对特殊值,解析处理。
  • 方案二:构造一个BloomFilter过滤器,初始化全量数据,当接到请求时,在BloomFilter中判断这个key是否存在,如果不存在,直接返回即可,无需再查询缓存和DB

3. 缓存热点

对于突发事件,大量用户同时去访问热点信息,这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象,甚至 Crash,我们称之为缓存热点。

热点缓存问题,就是超过了redis单个节点的访问容量上限,导致redis吃不消

详细参见《如果20万用户同时访问一个热点缓存,如何优化你的缓存架构?》

4. 大key问题

  • 当访问缓存时,如果key对应的value过大,读写、加载很容易超时,容易引发网络拥堵。
  • 另外缓存的字段较多时,每个字段的变更都会引发缓存数据的变更,频繁的读写,导致慢查询。
  • 如果大key过期被缓存淘汰失效,预热数据要花费较多的时间,也会导致慢查询。

在Redis中,一个字符串最大512MB,一个二级数据结构(例如hash、list、set、zset)可以存储大约40亿个(2^32-1)个元素,但实际中如果下面两种情况,我就会认为它是bigkey:

  • 字符串类型:它的big体现在单个value值很大,一般认为超过10KB就是bigkey。
  • 非字符串类型:哈希、列表、集合、有序集合,它们的big体现在元素个数太多。

4.1 如何优化bigkey


  1. big list: list1、list2、…listN
    big hash:可以将数据分段存储,比如一个大的key,假设存了1百万的用户数据,可以拆分成200个key,每个key下面存放5000个用户数据。这样分拆的意义在于分拆单次操作的压力,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响。

例如取模,拆分为key1、key2、key3,keyA经过取模后,发现对于key1,那么用key1去取数据;keyB经过取模后,发现对于key2,那么用key2去取数据

  1. 如果bigkey不可避免,也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要hmget,而不是hgetall),删除也是一样,尽量使用优雅的方式来处理。
  2. 优雅的删除大key
    大key要设置合理的过期时间,尽量不淘汰那些大key
    直接删除大Key或到期删除大Key的风险:生产环境中遇到过多次因业务删除大Key,导致Redis阻塞,出现故障切换和应用程序雪崩的故障。
    测试删除集合类型大Key耗时,一般每秒可清理100w~数百w个元素; 如果数千w个元素的大Key时,会导致Redis阻塞上10秒,可能导致集群判断Redis已经故障,出现故障切换;或应用程序出现雪崩的情况。

说明:Redis是单线程处理。单个耗时过大命令,导致阻塞其他命令,容易引起应用程序雪崩或Redis集群发生故障切换。所以避免在生产环境中使用耗时过大命令。

  1. 在redis4.0前,没有lazy free机制;针对扫描出来的大key,DBA只能通过hscan、sscan、zscan方式渐进删除若干个元素;但面对过期删除键的场景,这种取巧的删除就无能为力。
    Redis4.0新增了非常实用的lazy free特性,从根本上解决Big Key(主要指定元素较多集合类型Key)删除的风险,删除大键的过程不会阻塞正常请求。

5. 缓存数据一致性

系列文章:
《缓存与数据库双写不一致》《redis主从不一致问题》

6. 参考

《亿级系统的Redis缓存如何设计?》 参考主体