redis数据类型
redis过期策略
Redis会把设置了过期时间的key放入一个独立的字典里,在key过期时并不会立刻删除它。
Redis会通过如下两种策略,来删除过期的Key:
惰性删除
- 客户端访问某个Key时,Redis会检查该Key是否过期,若过期则删除。
定期扫描
- Redis默认每秒执行10次过期扫描(配置hz选项),扫描策略如下:
- 从过期字典中随机选择20个key;
- 删除20个key中已过期的key;
- 如果过期的key的比例超过25%,则重复步骤1;
redis淘汰策略
当Redis占用内存超出最大限制(maxmemory)时,可采用如下策略(maxmemory-policy),让Redis淘汰一些数据,以腾出空间继续提供读写服务:
- noeviction:对可能导致增大内存的命令返回错误(大多数写命令,DEL除外);
- volatitle-ttl:在设置了过期时间的key中,选择剩余寿命(TTL)最短的Key,将其淘汰;
- volatitle-lru:在设置了过期时间的key中,选择最少使用的key(LRU),将其淘汰;
- volatitle-random:在设置过期时间的key中,随机选择一些key,将其淘汰;
- allkeys-lru:在所有的key中,选择最少使用的Key(LRU),将其淘汰;
- allkeys-random:在所有的key中,随机选择一些key,将其淘汰;
LRU算法
- 维护一个链表,用于顺序存储被访问过的key。在访问数据时,最新访问过的key将被移
动倒表头, 即最近访问的key在表头,最少访问的key在表尾。
近似LRU算法(Redis)
- 给每个key维护一个时间戳,淘汰时随机采样5个key,从中淘汰掉最旧的key。如果还是超 出
内存限 制,则继续随机采样淘汰。优点:比LRU算法节约内存,却可以取得非常近似的 效果。
redis缓存穿透
场景
查询根本不存在的数据,使得请求直达存储层,导致其负载过大,甚至宕机
解决方案
-
缓存空对象
存储层未命中后,仍将空值存入缓存层。再次访问该数据时,缓存层会直接返回空值。 -
布隆过滤器
将所有存在的key提前存入布隆过滤器,在访问缓存层之前,先通过过滤器拦截,若请求的是不存在的key,则直接返回空值。
*
redis缓存击穿
场景
一份热点数据,它的访问量非常大。在其缓存失效瞬间,大量请求直达存储层,导致服务崩溃。
解决方案
-
加互斥锁
对数据的访问加互斥锁,当一个线程访问该数据时,其他线程只能等待。这个线程访问过后,缓存中的数据将被重建,届时其他线程就可以直接从缓存取值。 -
永不过期
不设置过期时间,所以不会出现上述问题,这是"物理"上的不过期。为每个value设置逻辑过期时间,当发现该值逻辑过期时,使用单独的线程重建缓存。
redis缓存雪崩
场景
由于某些原因,缓存层不能提供服务,导致所有的请求直达存储层,造成存储层宕机。
解决方案
-
避免同时过期,设置过期时间时,附加一个随机数,避免大量的key同时过期。
-
构建高可用的redis缓存,部署多个redis实例,个别节点宕机,依然可以保持服务的整体可用
-
构建多级缓存,增加一级缓存,在存储层前面多加一级屏障,降低请求直达存储层的几率。
-
启用限流和降级措施,对存储层增加限流措施,当请求超出限制时,对其提供降级服务(通常做法是返回一个默认值\空值)。
redis分布式锁
场景
修改时,经常需要先将数据读取到内存,在内存中修改后再存回去。在分布式应用中, 可能多个进程同时执行上述操作,而读取和修改非原子操作,所以会产生冲突。增加分 布式锁,可以解决此类问题。
基本原理
同步锁:在多个线程都能访问到的地方,做一个标记,标识该数据的访问权限。
分布式锁:在多个进程都能访问到的地方,做一个标记,标识该数据的访问权限。
实现方式
1. 基于数据库的实现分布式锁
2. 基于Redis实现分布式锁;
3. 基于Zookeeper实现分布式锁
实现分布式锁的原则
- 安全属性:独享。在任一时刻,只有一个客户端有锁。
- 活性A:无死锁。即便持有锁的客户端崩溃或者网络被分裂,锁任然可以被获取。
- 活性B:容错。只有大部分Redis节点都活着,客户端可以获取和释放锁
单Redis实现分布式锁
获取锁使用命令:
-
SET resource_name my_random_value NX PX 30000
-
NX:仅在key不存在时才执行成功,PX:设置锁的自动过期时间
-
通过Lua脚本释放锁
-
if redis.call(“get”,KEYS[1]) == ARGV[1] then
return redis.call(“del”,KEYS[1])
else return 0 end
-
可以避免删除别的客户端获取成功的锁:
A加索 ->A阻塞 -> 因超时释放锁 ->B加锁 ->A恢复 ->释放锁
多Redis实例实现分布式锁
-
Redlock算法,该算法有现场的实现,其java版本的库位Redisson。
-
获取当前Unix时间,以毫秒为单位。
-
依次尝试从N个实例,使用相同的key和随机值获取锁,并设置响应超时时间。如果服务器没有在规定时间内响应
-
客户端使用当前时间减去开始获取锁的时间,得到获取锁使用的时间。当且仅当大多数的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算取得成功。
-
如果取到了锁,key的真正有效时间等于有效时间减去获取锁使用的时间。
-
如果获取锁失败,客户端应该在所有的Redis实例上进行解锁。