前言
正常情况下,Redis集群中数据都是均匀分配到每个节点,请求也会均匀的分布到每个分片上,但在一些特殊场景中,比如外部爬虫、攻击、热点商品等,最典型的就是明星在微博上宣布离婚,吃瓜群众纷纷涌入路演,导致微博评论功能崩溃,这种短时间内某些key访问量过于大,对于这种相同的key会请求到同一台数据分片上,导致该分片负责较高成为瓶颈问题,导致雪崩等一些问题。
redis数据热点引起原因?
案例回答:
关于热点数据问题我有话要说,这个问题我早在刚刚学习使用 Redis 时就从已经意识到了,所以在使用时会刻意避免,坚决不会给自己挖坑,热点数据最大的问题会造成 Reids 集群负载不均衡(也就是数据倾斜)导致的故障,这些问题对于 Redis 集群都是致命打击。
先说说造成 Reids 集群负载不均衡故障的主要原因:
- 高访问量的 Key,也就是热 key,根据过去的维护经验一个 key 访问的 QPS 超过 1000 就要高度关注了,比如热门商品,热门话题等。
- 大 Value,有些 key 访问 QPS 虽然不高,但是由于 value 很大,造成网卡负载较大,网卡流量被打满,单台机器可能出现千兆 / 秒,IO 故障。
- 热点 Key + 大 Value 同时存在,服务器杀手。
那么热点 key 或大 Value 会造成哪些故障呢:
- 数据倾斜问题:大 Value 会导致集群不同节点数据分布不均匀,造成数据倾斜问题,大量读写比例非常高的请求都会落到同一个 redis server 上,该 redis 的负载就会严重升高,容易打挂。
- QPS 倾斜:分片上的 QPS 不均。
- 大 Value 会导致 Redis 服务器缓冲区不足,造成 get 超时。
- 由于 Value 过大,导致机房网卡流量不足。
- Redis 缓存失效导致数据库层被击穿的连锁反应。
reds缓存穿透
理解重在穿透吧,也就是访问透过redis直接经过mysql,通常是一个不存在的key,在数据库查询为null。每次请求落在数据库、并且高并发。数据库扛不住会挂掉。
解决方案:
- 可以将查到的null设为该key的缓存对象
- 可以根据明显错误的key在逻辑层就就行验证。
- 可以分析用户行为,是否为故意请求或者爬虫、攻击者。针对用户访问做限制。
- 用布隆过滤器(超大型hashmap)先过滤。
redis缓存雪崩
理解雪崩,就是某东西蜂拥而至的意思,像雪崩一样。在这里,就是redis缓存集体大规模集体失效,在高并发情况下突然使得key大规模访问mysql,使得数据库崩掉。可以想象下国家人口老年化。以后那天人集中在70-80岁,就没人干活了。国家劳动力就造成压力。
解决方案:
- 将key的过期时间后面加上一个随机数,让key均匀的失效。
- 考虑用队列或者锁让程序执行在压力范围之内,当然这种方案可能会影响并发量。
- 热点数据可以考虑不失效
redis缓存击穿
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,好像蛮力击穿一样。
缓存和击穿的不同
- 穿透的意思是想法绕过redis去使得数据库崩掉。
- 而击穿你可以理解为正面刚击穿,这种通常为大量并发对一个key进行大规模的读写操作。这个key在缓存失效期间大量请求数据库,对数据库造成太大压力使得数据库崩掉。就比如在秒杀场景下10000块钱的mac和100块的mac这个100块的那个订单肯定会被抢到爆,不断的请求(当然具体秒杀有自己处理方式这里只是举个例子)。
- 所以缓存击穿就是针对某个常用key大量请求导致数据库崩溃。
解决方案:
- 可以使用互斥锁避免大量请求同时落到db。
- 布隆过滤器,判断某个容器是否在集合中
- 可以将缓存设置永不过期(适合部分情况)
- 做好熔断、降级,防止系统崩溃。
真实项目中,热点数据问题如何准确定位的呢?
案例回答:
这个问题的解决办法比较宽泛,要具体看不同业务场景,比如公司组织促销活动,那参加促销的商品肯定是有办法提前统计的,这种场景就可以通过预估法。对于突发事件,不确定因素,Redis 会自己监控热点数据。大概归纳下:
- 提前获知法:根据业务,人肉统计 or 系统统计可能会成为热点的数据,如,促销活动商品,热门话题,节假日话题,纪念日活动等。
- redis客户端收集法,调用端通过计数的方式统计 key 的请求次数,但是无法预知 key 的个数,代码侵入性强。
- redis集群代理层统计。像 Twemproxy,codis 这些基于代理的 Redis 分布式架构,统一的入口,可以在 Proxy 层做收集上报,但是缺点很明显,并非所有的 Redis 集群架构都有 proxy。

- redis服务端收集。监控 Redis 单个分片的 QPS,发现 QPS 倾斜到一定程度的节点进行 monitor,获取热点 key, Redis 提供了 monitor 命令,可以统计出一段时间内的某 Redis 节点上的所有命令,分析热点 key,在高并发条件下,会存在内存暴涨和 Redis 性能的隐患,所以此种方法适合在短时间内使用;同样只能统计一个 Redis 节点的热点 key,对于集群需要汇总统计,业务角度讲稍微麻烦一点。
5. 修改redis源码。我发现 Redis4.0 为我们带来了许多新特性,其中便包括基于 LFU 的热点 key 发现机制,有了这个新特性,我们就可以在此基础上实现热点 key 的统计,这个只是我的个人思路。
如何解决热点数据问题
案例回答:
关于如何治理热点数据问题,解决这个问题主要从两个方面考虑,第一是数据分片,让压力均摊到集群的多个分片上,防止单个机器打挂,第二是迁移隔离
概括总结:
- key拆分。如果当前 key 的类型是一个二级数据结构,例如哈希类型。如果该哈希元素个数较多,可以考虑将当前 hash 进行拆分,这样该热点 key 可以拆分为若干个新的 key 分布到不同 Redis 节点上,从而减轻压力
- 迁移热点key。以 Redis Cluster 为例,可以将热点 key 所在的 slot 单独迁移到一个新的 Redis 节点上,这样这个热点 key 即使 QPS 很高,也不会影响到整个集群的其他业务,还可以定制化开发,热点 key 自动迁移到独立节点上,这种方案也较多副本。
- 热点key限流。对于读命令我们可以通过迁移热点 key 然后添加从节点来解决,对于写命令我们可以通过单独针对这个热点 key 来限流。
- 增加本地缓存。对于数据一致性不是那么高的业务,可以将热点 key 缓存到业务机器的本地缓存中,因为是业务端的本地内存中,省去了一次远程的 IO 调用。但是当数据更新时,可能会造成业务和 Redis 数据不一致。
redis支持丰富的数据类型,这些数据类型存储的大Value如何解决,线上有遇到这种情况吗?
案例回答:
相比热点 key 大概念,大 Value 的概念比好好理解,由于 Redis 是单线程运行的,如果一次操作的 value 很大会对整个 redis 的响应时间造成负面影响,因为 Redis 是 Key - Value 结构数据库,大 value 就是单个 value 占用内存较大,对 Redis 集群造成最直接的影响就是数据倾斜。
先说说多大的 Value 算大,根据公司基础架构给出的经验值可做以下划分:
- 大:string 类型 value > 10K,set、list、hash、zset 等集合数据类型中的元素个数 > 1000。
- 超大: string 类型 value > 100K,set、list、hash、zset 等集合数据类型中的元素个数 > 10000。
由于 Redis 是单线程运行的,如果一次操作的 value 很大会对整个 redis 的响应时间造成负面影响,所以,业务上能拆则拆,下面举几个典型的分拆方案:
- 一个较大的 key-value 拆分成几个 key-value ,将操作压力平摊到多个 redis 实例中,降低对单个 redis 的 IO 影响
- 将分拆后的几个 key-value 存储在一个 hash 中,每个 field 代表一个具体的属性,使用 hget,hmget 来获取部分的 value,使用 hset,hmset 来更新部分属性。
- hash、set、zset、list 中存储过多的元素
类似于场景一中的第一个做法,可以将这些元素分拆。
以 hash 为例,原先的正常存取流程是:
hget(hashKey,field)
hset(hashKey,field,value)
现在,固定一个桶的数量,比如 10000,每次存取的时候,先在本地计算 field 的 hash 值,模除 10000,确定该 field 落在哪个 key 上,核心思想就是将 value 打散,每次只 get 你需要的。
newHashKey = hashKey +(hash(field)%10000)
hset(newHashKey,field,value)
hget(newHashKey,field)