九、Redis 策略及应用问题
1、过期策略
-
Redis采用的过期策略是:定期删除+惰性删除
-
定时删除:
- 创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
- redis的所有定时器是用链表存储的
-
惰性删除:
- 只有当访问一个key时,才会判断该key是否已过期,过期则清除
-
定期删除:
- redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
2、淘汰策略
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的key(这个是最常用的)
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错。
3、缓存雪崩
-
缓存同⼀时间⼤⾯积的失效,所以,后⾯的请求都会落到数据库上,造成数据库短时间内承受⼤量请求⽽崩掉。
-
-
常用解决方法
- 使用锁或队列:用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况。
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 热点数据永不过期,有更新时更新缓存即可。
4、缓存击穿
-
一个Key非常热点,当这个Key失效瞬间,大量请求落到数据库,导致数据库崩溃。
-
-
常用解决方法:
- 设置热点数据永不过期,有更新时更新缓存。
- 加互斥锁
- 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。
- 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key
- 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;
- 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法。
5、 缓存穿透
-
key对应的数据在db中不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
-
常用解决方法:
- 缓存无效 key: 如果缓存和数据库都查不到某个 key 的数据就写⼀个到 redis 中去并设置短点的过期时间
- 采用布隆过滤器:把所有可能存在的请求的值都存放在布隆过滤器中,当⽤户请求过来,会先判断⽤户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会继续执行
6、缓存一致性
-
问题一:
先删除缓存,数据库还没有更新成功,问题:如果读取缓存,缓存不存在,去数据库中读取到的是旧值,缓存不一致发生。
-
问题二:
先更新数据库,再删除缓存,问题:如果更新数据库成功,如果删除缓存失败或者还没有来得及删除,那么,其他线程从缓存中读取到的就是旧值,还是会发生不一致。
-
总结:
- 如果数据对实时性、一致性要求不是很高的话,对缓存数据的时候加上过期时间即可。
- 缓存不是更新,而应该是删除。
- 如果数据库1小时内更新了1000次,那么缓存也要更新1000次,但是这个缓存可能在1小时内只被读取了1次,那么这1000次的更新有必要吗?
- 反过来,如果是删除的话,就算数据库更新了1000次,那么也只是做了1次缓存删除,只有当缓存真正被读取的时候才去数据库加载。
- 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。
- 如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。
7、分布式寻址算法
-
一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,如key%N,key是数据的key,N是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。就有以下的两种寻址算法:
-
redis cluster 的 hash slot 算法
-
一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)
-
-
算法流程:
- 构造一个长度为2^32的整数环,然后根据服务器节点的hash值,将服务器节点分布到这个环上。
- 当有需要缓存的数据时,根据该数据的key值计算得到hash值,并将其映射至环上。
- 在环上顺时针查找与该hash值最近的服务器节点,从而完成key到服务器节点的映射,并将数据存
储在该服务器节点上。 - 如果超过2^32次方仍然找不到服务器,则将数据保存到第一台服务器上。
-
当一个节点服务器宕机了,那么B上的请求就会落到下一个服务器上,可能会导致雪崩的情况
- 如果B节点宕机了,则B上的数据就会落到C节点上,C节点由于承担了B节点的数据,所以C节点的负载会变高,C节点很容易也宕机,这样依次下去,这样造成整个集群都挂了。如下图所示
-
引入了虚拟节点的概念: 在这个环上有很多“虚拟节点”,数据的存储是沿着环的顺时针方向找一个虚拟节点。
- 图中的A1、A2、B1、B2、C1、C2、D1、D2都是虚拟节点,机器A负载存储A1、A2的数据,机器B负载存储B1、B2的数据,机器C负载存储C1、C2的数据。由于这些虚拟节点数量很多,均匀分布,因此不会造成“雪崩”现象。
-
应用:
- 一致性哈希可以用于Memcache
- Memcache是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。其分布式由客户端实现。每个客户端维护一个服务器池,通过一致性哈希算法,将数据比较均匀地分布在池中的服务器上。
-