redis问题场景
一、缓存穿透
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;对接口次数做记录,超过一定的次数则禁止一定时间的访问。
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。当有新的数据覆盖了这个key的时候,这条数据就会变成永不过期的数据,这样可以防止攻击用户反复用同一个id暴力攻击
- 增加布隆过滤器,bloomfilter的优势是占用的空间小,使用的bit存储,本质是一个bitmap。性能高效,使用hash算法
二、缓存击穿
描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
- 设置热点数据永远不过期。
- 加互斥锁,让请求进入一个等待的时间
- 定时任务定时更新缓存
三、缓存雪崩
描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。统一规划有效期,失效时间均匀分布。
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 设置热点数据永远不过期。
- 加锁排队,通过jvm的锁或者分布式锁,对热点数据的高并发进行一个控制
- 双缓存方案,主缓存带有过期时间,备份缓存有效期长或者永不失效。注意主备的数据更新必须一致
- 定时去更新一些缓存数据,对一些实效性要求不高的缓存,可以通过容器启动初始化进行加载,定时任务去更新
四、热点key
缓存中的某些Key(可能对应用与某个促销商品)对应的value存储在集群中一台机器,使得所有流量涌向同一机器,成为系统的瓶颈,该问题的挑战在于它无法通过增加机器容量来解决。
解决方案:
- 将热点key对应的value缓存到本地,并且设置一定的失效时长。对于每次读的请求,判断一下本地key的数据是否存在,将redis服务器的压力降低
- 将热点key分散为多个子key,然后存储到缓存集群的不同机器上,这些子key对应的value都和热点key是一样的。当通过热点key去查询数据时,通过某种hash算法随机选择一个子key,然后再去访问缓存机器,将热点分散到了多个子key上。
五、数据倾斜
数据倾斜分为两种:
- 数据量倾斜:在某些情况下,实例上的数据分布不均衡,某个实例上的数据特别多。
- 数据访问倾斜:虽然每个集群实例上的数据量相差不大,但是某个实例上的数据是热点数据,被访问得非常频繁。
造成数据倾斜主要有以下原因:
- 在某个实例上保存了 bigkey
- Slot 分配不均
- Hash Tag
- 热点数据的存在导致访问量倾斜
bigkey导致倾斜
为了避免 bigkey 造成的数据倾斜,一个根本的应对方法是,我们在业务层生成数据时,要尽量避免把过多的数据保存在同一个键值对中。
此外,如果 bigkey 是集合类型,可以把 bigkey 拆分成很多个小的集合类型数据,分散保存在不同的实例上。
slot分配不均导致倾斜 如果集群运维人员没有均衡地分配 Slot,就会有大量的数据被分配到同一个 Slot 中,而同一个 Slot 只会在一个实例上分布,这就会导致,大量数据被集中到一个实例上,造成数据倾斜。如果某一个实例上有太多的 Slot,我们就可以使用迁移命令把这些 Slot 迁移到其它实例上。 Hash Tag 导致倾斜 Hash Tag 是指加在键值对 key 中的一对花括号{}。这对括号会把 key 的一部分括起来,客户端在计算 key 的 CRC16 值时,只对 Hash Tag 花括号中的 key 内容进行计算。如果没用 Hash Tag 的话,客户端计算整个 key 的 CRC16 的值。
使用 Hash Tag 的好处是,如果不同 key 的 Hash Tag 内容都是一样的,那么,这些 key 对应的数据会被映射到同一个 Slot 中,同时会被分配到同一个实例上。
但是,使用 Hash Tag 的潜在问题,就是大量的数据可能被集中到一个实例上,造成数据访问倾斜
数据访问倾斜 通常来说,热点数据以服务读操作为主,在这种情况下,我们可以采用热点数据多副本的方法来应对。这个方法的具体
这个方法的具体做法是,我们把热点数据复制多份,在每一个数据副本的 key 中增加一个随机前缀,让它和其它副本数据不会被映射到同一个 Slot 中。这样一来,热点数据既有多个副本可以同时服务请求,同时,这些副本数据的 key 又不一样,会被映射到不同的 Slot 中。在给这些 Slot 分配实例时,我们也要注意把它们分配到不同的实例上,那么,热点数据的访问压力就被分散到不同的实例上了。
但是需要注意,热点数据多副本方法只能针对只读的热点数据。如果热点数据是有读有写的话,就不适合采用多副本方法了,因为要保证多副本间的数据一致性,会带来额外的开销。
对于有读有写的热点数据,我们就要给实例本身增加资源了,例如使用配置更高的机器,来应对大量的访问压力。