Redis
1.redis高可用方案
a. 主从
哨兵模式:
sentinel,哨兵是 redis 集群中非常重要的一个组件,主要有以下功能:
- 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
- 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
- 故障转移:如果 master node 挂掉了,会自动转移到slave node 上。
- 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。
哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
-
故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的
-
哨兵通常需要3个实例,来保证自己的健壮性。
-
哨兵+redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
-
对于哨兵+redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
b.Redis sharding是Redis Cluster出来之前,业界普遍使用的多Redis实例集群方法。其主要思想是采用哈希算法将Redis数据的key进行散列,通过hash的数,特定的key会映射到特定的Redis节点上。Java redis客户端驱动jedis,支持Redis sharding功能,即Shardedjedis以及结合缓存池的ShardedjedisPool
优点
优势在于非常简单,服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强
缺点
由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战,客户端sharding不支持动态增删节点。服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。连接不能共享,当应用规模增大时,资源浪费制约优化
c.Redis Cluster是一种服务端sharding技术,3.0版本开始正式提供。采用slot(槽)的概念,一共分成16384个槽。将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行
方案说明
- 通过哈希的方式,将数据分片,每个节点均分存储一定哈希槽(哈希值)区间的数据,默认分配了16384 个槽位
- 每份数据分片会存储在多个互为主从的多节点上
- 数据写入先写主节点,再同步到从节点(支持配置为阻塞同步)
- 同一分片多个节点间的数据不保持强一致性
- 读取数据时,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
- 扩容时需要需要把I日节点的数据迁移一部分到新节点
在 redis cluster 架构下,每个redis 要放开两个端口号,比如一个是6379,另外一个就是加1w的端口号,比如16379.
16379 端口号是用来进行节点间通信的,也就是 cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用重少的网络带宽和处理时间。
优点
- 无中心架构,支持动态扩容,对业务透明
- 具备Sentinel的监控和自动Failover(故障转移)能力
- 客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。高性能,客户端直连redis服务,免去了proxy代理的损耗
缺点
- 运维也很复杂,数据迁移需要人工干预
- 只能使用0号数据库
- 不支持批量操作(pipeline管道操作)
- 分布式逻辑和存储模块耦合等
2.如何避免缓存穿透、缓存击穿、缓存雪崩?
缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
解决方案:
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存。
- 缓存预热
- 互斥锁
缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案:
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-nul,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
- 采用布降过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个bitmap 拦截掉,从而避免了对底层存储系统的查询压力
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由干并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案
- 设置热点数据永远不过期
- 加互斥锁
3.为什么使用缓存 ?
1、高性能
2、高可用
4.如何保证Redis与数据库的数据一致?
当我们对数据进行修改的时候,到底是先删缓存,还是先写数据库?
1、如果先删缓存,再写数据库: 在高并发场景下,当第一个线程删除了缓存,还没有来得及写数据库,第二个线程来读取数据,会发现缓存中的数据为空,那就会去读数据库中的数据(旧值,脏数据),读完之后,把读到的结果写入缓存(此时,第一个线程已经将新的值写到缓存里面了),这样缓存中的值就会被覆盖为修改前的脏数据。
总结:在这种方式下,通常要求写操作不会太频繁。
解决方案:1》先操作缓存,但是不删除缓存。将缓存修改为一个特殊值(-999)。客户端读缓存时,发现是默认值,就休眠一小会,再去查一次Redis。 -》 特殊值对业务有侵入。 休眠时间,可能会多次重复,对性能有影响。
2》延时双删。 先删除缓存,然后再写数据库,休眠一小会,再次删除缓存。-》 如果数据写操作很频繁,同样还是会有脏数据的问题。
2、先写数据库,再删缓存: 如果数据库写完了之后,缓存删除失败,数据就会不一致。
总结: 始终只能保证一定时间内的最终一致性。
解决方案: 1》给缓存设置一个过期时间 问题:过期时间内,缓存数据不会更新。
2》引入MQ,保证原子操作。
解决方案:将热点数据缓存设置为永不过期,但是在value当中写入一个逻辑上的过期时间,另外起一个后台线程,扫描这些key,对于已逻辑上过期的缓存,进行删除。
5.redis 主从同步机制
-
从节点执行slaveof masterIp port,保存主节点信息
-
从节点中的定时任务发现主节点信息,建立和主节点的socket连接
-
从节点发送信号,主节点返回,两边能互相通信
-
连接建立后,主节点将所有数据发送给从节点(数据同步)
-
主节点把当前的数据同步给从节点后,便完成了复制过程。接下来,主节点就会持续的把写命令发
送给从节点,保证主从数据一致性。
runId:每个redis节点启动都会生成唯一的uuid,每次redis重启后,runId都会发生变化。
offset:主从节点各自维护自己的复制偏移量offset,当主节点有写入命令时,offset=offset+命令的字
节长度。从节点在收到主节点发送的命令后,也会增加自己的offset,并把自己的offset发送给主节
点。主节点同时保存自己的offset和从节点的offset,通过对比offset来判断主从节点数据是否一致。
repl_backlog_size:保存在主节点上的一个固定长度的先进先出队列,默认大小是1MB。
全量复制:
-
从节点发送psync命令,psync runid offset(由于是第一次,runid为?,offset为-1)
-
主节点返回FULLRESYNC runId offset,runId是主节点的runId,offset是主节点目前的offset。
从节点保存信息
-
主节点启动bgsave命令fork子进程进行RDB持久化
-
主节点将RDB文件发送给从节点,到从节点加载数据完成之前,写命令写入缓冲区
-
从节点清理本地数据并加载RDB,如果开启了AOF会重写AOF
部分复制:
- 复制偏移量:psync runid offset
- 复制积压缓冲区:当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
- 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);
- 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。
过程原理:
6.简述Redis事务实现
1、事务开始
MULTI命令的执行,标识着一个事务的开始。MULTI命令会将客户端状态的flags
属性中打开
REDIS_MULTI
标识来完成的。
2、命令入队
当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。如果客
户端发送的命令为MULTI、EXEC、WATCH、DISCARD中的一个,立即执行这个命令,否则将命令放入一
个事务队列里面,然后向客户端返回 QUEUED 回复
-
如果客户端发送的命令为 EXEC、DISCARD、WATCH、MULTI 四个命令的其中一个,那么服务器
立即执行这个命令。
-
如果客户端发送的是四个命令以外的其他命令,那么服务器并不立即执行这个命令。
首先检查此命令的格式是否正确,如果不正确,服务器会在客户端状态(redisClient)的 flags 属
性关闭 REDIS_MULTI 标识,并且返回错误信息给客户端。
如果正确,将这个命令放入一个事务队列里面,然后向客户端返回 QUEUED 回复
事务队列是按照FIFO的方式保存入队的命令
3、事务执行
客户端发送 EXEC 命令,服务器执行 EXEC 命令逻辑。
-
如果客户端状态的 flags 属性不包含 REDIS_MULTI 标识,或者包含 REDIS_DIRTY_CAS 或者
REDIS_DIRTY_EXEC 标识,那么就直接取消事务的执行。
-
否则客户端处于事务状态(flags 有 REDIS_MULTI 标识),服务器会遍历客户端的事务队列,然
后执行事务队列中的所有命令,最后将返回结果全部返回给客户端;
redis 不支持事务回滚机制,但是它会检查每一个事务中的命令是否错误。
Redis 事务不支持检查那些程序员自己逻辑错误。例如对 String 类型的数据库键执行对 HashMap 类型
的操作!
-
WATCH 命令是一个乐观锁,可以为 Redis 事务提供 check-and-set (CAS)行为。可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
-
MULTI命令用于开启一个事务,它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有 队列中的命令才会被执行。
-
EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。
-
通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中出。
-
UNWATCH命令可以取消watch对所有key的监控。
7.redis分布式锁实现
setnx+setex :存在设置超时时间失败的情况,导致死锁
set(key,value,nx,px):将setnx+setex变成原子操作
问题:
- 任务超时,锁自动释放,导致并发问题。使用redisson解决(看门狗监听,自动续期)
- 以及加锁和释放锁不是同一个线程的问题。在value中存入uuid(线程唯一标识),删除锁时判断该标识(使用lua保证原子操作)
- 不可重入,使用redisson解决(实现机制类似AQS,计数)
- 异步复制可能造成锁丢失,使用redLock解决
- 顺序向五个节点请求加锁
- 根据一定的超时时间来推断是不是跳过该节点
- 三个节点加锁成功并且花费时间小于锁的有效期
- 认定加锁成功
8.简述redis九大数据结构
String:字符串
List:列表
Hash:哈希表
Set:无序集合
Sorted Set:有序集合
bitmap:布隆过滤器
GeoHash:坐标,借助Sorted Set实现,通过zset的score进行排序就可以得到坐标附近的其它元素,
通过将score还原成坐标值就可以得到元素的原始坐标
HyperLogLog:统计不重复数据,用于大数据基数统计
Streams:内存版的kafka
9.缓存过期都有哪些策略
定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立
即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响
应时间和吞吐量
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省
CPU资源,但是很消耗内存、许多的过期数据都还存在内存中。极端情况可能出现大量的过期key没有
再次被访问,从而不会被清除,占用大量内存。
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key(是随机的),
并清除其中已过期的key。该策略是定时过期和惰性过期的折中方案。通过调整定时扫描的时间间隔和
每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
分桶策略:定期过期的优化,将过期时间点相近的key放在一起,按时间扫描分桶。
10.分布式系统中常用的缓存方案有哪些
客户端缓存:页面和浏览器缓存,APP缓存,H5缓存,localStorage 和 sessionStorage
CDN缓存:内容存储:数据的缓存,内容分发:负载均衡
nginx缓存:静态资源
服务端缓存:本地缓存,外部缓存
数据库缓存:持久层缓存(mybatis,hibernate多级缓存),mysql查询缓存
操作系统缓存:Page Cache、Buffer Cache
11.布隆过滤器原理,优缺点
位图:int[10],每个int类型的整数是4*8=32个bit,则int[10]一共有320 bit,每个bit非0即1,初始化时都是0
添加数据时,将数据进行hash得到hash值,对应到bit位,将该bit改为1,hash函数可以定义多个,则一个数据添加会将多个(hash函数个数)bit改为1,多个hash函数的目的是减少hash碰撞的概率
查询数据:hash函数计算得到hash值,对应到bit中,如果有一个为0,则说明数据不在bit中,如果都为1,则该数据可能在bit中
优点:
- 占用内存小
- 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关
- 哈希函数相互之间没有关系,方便硬件并行运算
- 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
- 数据量很大时,布隆过滤器可以表示全集
- 使用同一组散列函数的布隆过滤器可以进行交、并、差运算
缺点:
- 误判率,即存在假阳性(False Position),不能准确判断元素是否在集合中
- 不能获取元素本身
- 一般情况下不能从布隆过滤器中删除元素
12.Redis集群策略
Redis提供了三种集群策略:
-
主从模式:这种模式比较简单,主库可以读写,并且会和从库进行数据同步,这种模式下,客户端直接连主库或菜个从库,但是但主库或从库宕机后,客户端需要手动修改IP,另外,这种模式也比较难进行扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可能支持特大数据量
-
哨兵模式:这种模式在主从的基础上新增了哨兵节点,但主库节点容机后,哨兵会发现主库节点容机,然后在从库中选择一个库作为进的主库,另外哨兵也可以做集群,从而可以保证但某一个销兵节点宕机后,还有其他哨兵节点可以继续工作,这种模式可以比较好的保证Redis集群的高可用,但是仍然不能很好的解决Redis的容量上限问颐。
-
Custer模式:Custer模式是用得比较多的模式,它支持多主多从,这种模式会按照key进行槽位的分配,可以使得不同的key分散到不同的主节点上,利用这种模式可以使得整个集群支持更大的数据容量,同时每个主节点可以拥有自己的多个从节点,如果该主节点宕机,会从它的从节点中选举一个新的主节点。
对于这三种模式,如果Redis要存的数据量不大,可以选择哨兵模式,如果Redis要存的数据量大,并且需要持续的扩容,那么选择Cluster模式。
13.常见的缓存淘汰算法
FIFO(First In First Out,先进先出),根据缓存被存储的时间,离当前最远的数据优先被淘汰;
LRU(Least Recently Used,最近最少使用),根据最近被使用的时间,离当前最远的数据优先被淘汰;
LFU(Least Frequently Used,最不经常使用),在一段时间内,缓存数据被使用次数最少的会被淘汰。
14.redis主从复制的核心原理
通过执行slaveof命令或设置slaveof选项,让一个服务器去复制另一个服器的敏据,生数倨库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
全量复制:
- 主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘I0的
- 主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗
- 从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令;如果从节点执行bgrewriteaof,也会带来额外的消耗
部分复制:
- 复制偏移量:执行复制的双方,主从节点,分别会维护一个复制移量offset
- 复制积压缓冲区:主节点内部维护了一个固定长度的、先进先出(FIFO)队列 作为复制积压缓冲区,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
- 服务器运行ID(runid):每个Redis节点,都有其运行ID,运行ID由节点在启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会将主节点的运行ID存起来。从节点Redis断开重连的时候,就是根据运行ID来判断同步的进度:
- 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部。分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况);
- 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制。
15.Redis的数据结构及使用场景
- 字符串:可以用来做展简单的数据缓存,可以须存某个简单的字符串,也可以缓存某个son格式的字符串,Reds分布式铁的实现就利用了这种数据结构,还包括可以实现计数器、Session共享、分布式ID
- 哈希表:可以用来存储一些key-value对,更适合用来存储对象
- 列表:Redis的列表通过命令的组合,既可以当做栈,也可以当做队列来使用,可以用来缓存类似微信公众号、微博等消息流数据
- 集合:和列表类似,也可以存储多个元素,但是不能重复,集合可以进行交集、并集、差集操作,从而可以实现类似,我和某人共同关注的人、朋友圈点替等功能
- 有序集合:集合是无序的,有序集合可以设置顺序,可以用来实现排行榜功能
16.redis的持久化机制
RDB:Redis DataBase 将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘。
手动触发:
- save命令,使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所 以在生产环境一定要慎用
- bgsave命令,fork出一个子进程执行持久化,主进程只在fork过程中有短暂的阻塞,子进程创建 之后,主进程就可以响应客户端请求了
自动触发:
- save m n :在 m 秒内,如果有 n 个键发生改变,则自动触发持久化,通过bgsave执行,如果设置多个、只要满足其一就会触发,配置文件有默认配置(可以注释掉)
- flushall:用于清空redis所有的数据库,flushdb清空当前redis所在库数据(默认是0号数据库),会 清空RDB文件,同时也会生成dump.rdb、内容为空
- 主从同步:全量同步时会自动触发bgsave命令,生成rdb发送给从节点
优点:
- 整个Redis数据库将只包含一个文件 dump.rdb,方便持久化。
- 容灾性好,方便备份。
- 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进 程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
- 相对于数据集大时,比 AOF 的启动效率更高。
缺点:
- 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢 失。所以这种方式更适合数据要求不严谨的时候)
- 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导 致整个服务器停止服务几百毫秒,甚至是1秒钟。会占用cpu
AOF:Append Only File 以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录,调操作系统命令进程刷盘
- 所有的写命令会追加到 AOF 缓冲中。
- AOF 缓冲区根据对应的策略向硬盘进行同步操作。
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- 当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
同步策略:
- 每秒同步:异步完成,效率非常高,一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢 失
- 每修改同步:同步持久化,每次发生的数据变化都会被立即记录到磁盘中,最多丢一条
- 不同步:由操作系统控制,可能丢失较多数据
优点:
- 数据安全
- 通过 append 模式写文件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过 redis-check-aof 工具解决数据一致性问题。
- AOF 机制的 rewrite 模式。定期对AOF文件进行重写,以达到压缩的目的
缺点:
- AOF 文件比 RDB 文件大,且恢复速度慢。
- 数据集大的时候,比 rdb 启动效率低。
- 运行效率没有RDB高
AOF文件比RDB更新频率高,优先使用AOF还原数据。
AOF比RDB更安全也更大
RDB性能比AOF好
如果两个都配了优先加载AOF
17.redis集群方案
同 1.redis高可用方案
18.Redis线程模型,单线程为什么快
Redis基于Reactor模式开发了网络事件处理器、文件事件处理器 file event handler。它是单线程的, 所以 Redis 才叫做单线程的模型,它采用IO多路复用机制来同时监听多个Socket,根据Socket上的事件 类型来选择对应的事件处理器来处理这个事件。可以实现高性能的网络通信模型,又可以跟内部其他单 线程的模块进行对接,保证了 Redis 内部的线程模型的简单性。
文件事件处理器的结构包含4个部分:多个Socket、IO多路复用程序、文件事件分派器以及事件处理器 (命令请求处理器、命令回复处理器、连接应答处理器等)。
多个 Socket 可能并发的产生不同的事件,IO多路复用程序会监听多个 Socket,会将 Socket 放入一个 队列中排队,每次从队列中有序、同步取出一个 Socket 给事件分派器,事件分派器把 Socket 给对应的 事件处理器。
然后一个 Socket 的事件处理完之后,IO多路复用程序才会将队列中的下一个 Socket 给事件分派器。文 件事件分派器会根据每个 Socket 当前产生的事件,来选择对应的事件处理器来处理。
- Redis启动初始化时,将连接应答处理器跟AE_READABLE事件关联。
- 若一个客户端发起连接,会产生一个AE_READABLE事件,然后由连接应答处理器负责和客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件和命令请求处理器关联,使得客户端可以向主服务器发送命令请求。 当客户端向Redis发请求时(不管读还是写请求),客户端socket都会产生一个AE_READABLE事 件,触发命令请求处理器。处理器读取客户端的命令内容, 然后传给相关程序执行。
- 当Redis服务器准备好给客户端的响应数据后,会将socket的AE_WRITABLE事件和命令回复处理器 关联,当客户端准备好读取响应数据时,会在socket产生一个AE_WRITABLE事件,由对应命令回复处理器处理,即将准备好的响应数据写入socket,供客户端读取。
- 命令回复处理器全部写完到 socket 后,就会删除该socket的AE_WRITABLE事件和命令回复处理器 的映射。
单线程快的原因:
1)纯内存操作
2)核心是基于非阻塞的IO多路复用机制
3)单线程反而避免了多线程的频繁上下文切换带来的性能问题
19.Redis如何设置key的过期时间
redis设置key的过期时间:1、EXPIRE。2、SETEX
实现原理:
- 定期删除: 每隔一段时间,执行一次删除过期key的操作。
- 懒汉式删除: 当使用get、getset等指令去获取数据时,判断key是否过期。过期后,就先把key删除,再执行后面的操作。
Redis是将两种方式结合来使用。
懒汉式删除
定期删除:平衡执行频率和执行时长。
定期删除时会遍历每个database(默认16个),检查当前库中指定个数的key(默认是20个)。随机抽查这些key,如果有过期的,就删除。