缓存击穿是指,如果对某个热点数据的大量请求无法在缓存中进行处理,那么这些请求一下子都要到后端数据库去请求数据,导致了底层数据库压力激增,进而影响数据库处理其他请求。这种情况,经常发生在热点数据过期失效的时候,如下图所示:

redis的hosts后面是字母 redis hot key_redis

问题的主要原因是某个热点的key失效了,导致大并发集中打在数据库上。所以要从两个方面解决, 

  • 延长热点key的过期时间或者设置永不过期,避免热点数据失效;
  • 利用互斥锁保证同一时刻只有一个客户端可以查询底层数据库的这个数据,同时一旦查到数据就马上缓存至Redis内,避免其他大量请求同时穿过Redis访问底层数据库;

 

缓存雪崩

缓存雪崩是缓存击穿的"大面积"版,缓存击穿是数据库缓存到Redis内的热点数据失效导致大量并发查询穿过redis直接击打到底层数据库,而缓存雪崩是指Redis中大量的key几乎同时过期,然后大量并发查询穿过redis击打到底层数据库上,此时数据库层的负载压力会骤增,我们称这种现象为"缓存雪崩"。事实上缓存雪崩相比于缓存击穿更容易发生,对于大多数公司来讲,同时超大并发量访问同一个过时key的场景的确太少见了,而大量key同时过期,大量用户访问这些key的几率相比缓存击穿来说明显更大。

 

redis的hosts后面是字母 redis hot key_数据库_02

解决方案:

  • 在可接受的时间范围内随机设置key的过期时间,分散key的过期时间,以防止大量的key在同一时刻过期;
  • 对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,让一部分查询先将数据缓存起来;
  • 延长热点key的过期时间或者设置永不过期,这一点和缓存击穿中的方案一样;

热点数据 

上面的这两个问题其实都是由热点数据引起的。在一个访问量很大的系统中,热key一直以来都是一个不可避免的问题。比如电商中某些商品成了爆款,微博的热点,或者是秒杀时瞬间大量开启的爬虫用户, 这些突发的无法预先感知的热key会给系统带来很大的风险。 那么如何来解决热点数据带来的问题呢?以下提供几种思路。

我们分别以真正有用的hotkey,刷子用户、限流等典型的场景来分析。

真正的redis热key:

1)使用二级缓存,读取到redis的热键值后就直接保留在本地缓存中,设置个过期时间,这样访问到来的时候可以直接从本地缓存获取数据,不需要访问redis。

2)改写redis源码加入热点探测功能,有热key时推送到客户端。问题主要是不通用,且有一定难度。

3)改写redis客户端,在本地探测热点key,是热key的就本地缓存起来并通知集群内其他机器。

刷子爬虫用户:

常见的有:

1)日常累积后,将这批黑名单通过配置中心推送到jvm内存。存在滞后无法实时感知的问题。

2)通过本地累加,进行实时计算,单位时间内超过阈值的算刷子。如果服务器比较多,存在用户请求被分散,本地计算达不到甄别刷子的问题。

3)引入其他组件如redis,进行集中式累加计算,超过阈值的拉取到本地内存。问题就是需要频繁读写redis,依旧存在redis的性能瓶颈问题。

限流:

1)单机维度的接口限流多采用本地累加计数

2)集群维度的多采用第三方中间件,如sentinel

3)网关层的,如Nginx+lua

以上的问题关键的地方在于如何识别hotkey,只要识别出来了hotkey,那么根据具体的场景做相应的处理,要么做二级缓存,要么识别出刷子爬虫用户后拉黑。

对于如何识别hotkey,可以参考京东的hotkey识别组件:JDHotkey