hello world

stay foolish, stay hungry

缓存常见问题

Redis 一个比较常见的场景就是作为应用的缓存,合理的利用缓存可以提升系统性能,使用过程中比较常见的问题有:缓存穿透、 缓存雪崩、 热点key问题。

缓存击穿

在做数据库缓存的使用场景中,我们通常会先从 Redis 中查询数据,如果在 Redis 中查询不到数据,则会查询数据库中的数据。如果查询的是不存在的数据,比如 id 是「-1」的用户,则会导致每次请求都会去数据库中查询,如果这种请求过多,会导致数据库压力过大,造成缓存击穿问题。

解决「缓存击穿」问题大致有三种方案,从业务角度解决、缓存空值和采用布隆过滤器。

从业务角度解决

可以判断查询的数据是否符合规范,比如小于 0 的 ID 可以不进行查询。查询前先校验要查询的 ID 是否是符合规范的 ID。

缓存空值

查询数据库之后,对于数据库中不存在的数据我们可以把 Redis 中对应的 key 设置为一个空值,这样再次收到相同的请求就可以命中缓存了。需要注意的是如果对应的 key 设置了新的有效的值,则需要将缓存中的数据删掉。这种方案有一个问题是,如果攻击者采用不同的伪造请求进行攻击时,可能会导致缓存空间暴涨,进而影响了正常的数据查询。

采用布隆过滤器

布隆过滤器(Bloom Filter)实际上是一个很长的二进制向量和一些列映射函数,可以检测一个元素是否在一个集合中。优点是空间利用率和查询效率都比较高;缺点是有一定的误识别率和删除困难。在4.0以上版本 Redis 中可以通过 module 加载 RedisBloom 来实现布隆过滤器。

缓存雪崩

我们使用 Redis 通常会设置 key 的过期时间,如果大量的 key 设置了相同的过期时间,可能会导致同一时间大量key失效,导致大量请求到了数据库,造成数据库瞬时压力过大,造成缓存雪崩的问题。另外缓存故障也可能导致缓存雪崩问题,比如Redis不可用等。

解决方案

缓存失效导致的缓存雪崩问题可以通过设置不同的过期时间的方式来解决。比如设置缓存 key 的过期时间可以采用「固定的时间」加随机数的方式。另外应当使用集群模式,比如 Redis Sentinel 和 Redis Cluster ,避免单点问题。

热点key

某些场景,比如秒杀、 微博大V、 热点新闻等,会导致缓存中某个key访问量特别大,极端情况可能会超出单台 server 的承载能力。

服务端缓存

对于读多写少的场景,可以将热点数据缓存到服务端本地,可以减少对后端缓存或数据库的压力。本地缓存可以使用 Map 数据结构,或者使用成熟的开源方案,比如 Guava 中的 LocalCacheLocalCache 自带key过期功能,使用也比较方便。

本地缓存方案简单,可以明显提升系统系能,但也可能带来「脏数据」问题,比如数据库和全局缓存中某条数据更新或删除,本地缓存更新不及时,依旧返回的是旧数据。可以搭配「定时轮询」或「发布-订阅」的方式来解决本地缓存更新不及时的问题。本地server可以定期轮询缓存和数据库中相应key的数据,来更新本地缓存;或者当数据变更时通过消息队列「广播」或者 zk 来通知所有 server 更新数据。

拆分key

在 Redis 集群中,key 按一定的分片算法分布在不同的分片上,我们可以将热点的 key 拆分成多个 key,比如将 key 加上后缀或前缀,以让key分布在不同的分片,来分担单个分片的压力。热点 key 按「读多写少」或「写多读少」可以分为「读热点key」和「写热点key」。对于读取热点 key,更新时需要更新所有 key 的数据,通过放大写入的方式来提高读取性能;对于写热点key,更新时选择其中一个key做更新,读取时需要合并所有key,采用读放大的方式来提高写入性能。

热点key发现

人工判断

某些特定的场景可以预测出可能存在的热点 key,比如秒杀的产品或者大V等等,可以提前在系统中提前配置热点 key,通过配置中心或队列下发到服务端。

自动发现热key

可以通过监控打点来检测热点 key,当 key 的访问频率达到某个阈值后,将其标记为热点 key,通过配置中心或队列下发到服务端。此外 Redis 4.0 新增了 allkey-lfu 和 volatile-lfu 两种数据过期策略,并且可以通过 OBJECT 命令获取某个 key 的访问频率,在客户端可以通过--hotkeys快速找到热点 key, 使用方式 redis-cli -h <host> -a <password> --hotkeys

通常会采用人工配置和自动发现一起的方式来发现热点key,通过人工的方式提前配置已知的热key,并通过流式计算等方式自动发现潜在的热key。

总结

应用缓存是提升系统性能的有效手段,由缓存引入的问题可以根据业务和现实情况不同,应该选择适合自己的方案来解决。