# 1. 集群
先来简单了解下redis中提供的集群策略, 虽然redis有持久化功能能够保障redis服务器宕机也能恢复并且只有少量的数据损失,
但是由于所有数据在一台服务器上,如果这台服务器出现硬盘故障,那就算是有备份也仍然不可避免 数据丢失的问题。
在实际生产环境中,我们不可能只使用一台redis服务器作为我们的缓存服务器,必须要多台实现集群,避免出现 单点故障;
# 2. 主从复制
复制的作用是把redis的数据库复制多个副本部署在不同的服务器上,如果其中一台服务器出现故障,
也能快速迁移到其他服务器上提供服务。
复制功能可以实现当一台redis服务器的数据更新后,自动将新的数据同步到其他服务器上。
主从复制就是我们常见的master/slave模式, 主数据库可以进行读写操作,
当写操作导致数据发生变化时会自动将 数据同步给从数据库。
而一般情况下,从数据库是只读的,并接收主数据库同步过来的数据。 一个主数据库可以有多个从数据库。

# 3. 配置
在redis中配置master/slave是非常容易的,只需要在从数据库的配置文件中加入slaveof 主数据库地址和端口。
而 master 数据库不需要做任何改变.
准备两台服务器,分别安装redis , server1 server2
1. 在server2的redis.conf文件中增加 slaveof server1-ip 6379 同时将bindip 修改成0.0.0.0,允许所有ip访问
2. 启动server2
3. 访问server2的redis客户端,输入 INFO replication
# 4. 全量复制
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤
完成上面几个步骤后就完成了slave服务器数据初始化的所有操作,slave服务器此时可以接收来自用户的读请求。
master/slave 复制策略是采用乐观复制,也就是说可以容忍在一定时间内master/slave数据的内容是不同的,
但是两者的数据会最终同步。具体来说,redis的主从同步过程本身是异步的,
意味着master执行完客户端请求的命令后会立即返回结果给客户端,然后异步的方式把命令同步给slave。
这一特征保证启用master/slave后 master的性能不会受到影响。
但是另一方面,如果在这个数据不一致的窗口期间,master/slave因为网络问题断开连接,
而这个时候,master 是无法得知某个命令最终同步给了多少个slave数据库。
不过redis提供了一个配置项来限制只有数据至少同步给多少个slave的时候,master才是可写的.
min-slaves-to-write 3
表示只有当3个或以上的slave连接到master,master才是可写的
min-slaves-max-lag 10
表示允许slave最长失去连接的时间,如果10秒还没收到slave的响应,则master认为该 slave以断开

# 5. 增量复制
从redis 2.8开始,就支持主从复制的断点续传,
如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份
master node会在内存中创建一个backlog,master和slave都会保存一个replica offset还有一个master id,
offset 就是保存在backlog中的。
如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制,
但是如果没有找到对应的offset,那么就会执行一次全量同步。
# 6. 无硬盘复制
前面我们说过,Redis复制的工作原理基于RDB方式的持久化实现的,也就是master在后台保存RDB快照,
slave接收到rdb文件并载入,但是这种方式会存在一些问题
1. 当master禁用RDB时,如果执行了复制初始化操作,Redis依然会生成RDB快照,
当master下次启动时执行该 RDB文件的恢复,但是因为复制发生的时间点不确定,
所以恢复的数据可能是任何时间点的,就会造成数据出现问题.
2. 当硬盘性能比较慢的情况下(网络硬盘),那初始化复制过程会对性能产生影响
因此2.8.18以后的版本,Redis引入了无硬盘复制选项,可以不需要通过RDB文件去同步,
直接发送数据,通过以下配置来开启该功能
repl-diskless-sync yes
master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了
# 7. 哨兵机制
1. 当master遇到异常终端后,需要从slave中选举一个新的master继续对外提供服务,这种机制在前面 提到过N次,
比如在zk中通过leader选举、kafka中可以基于zk的节点实现master选举。
所以在redis中也需要一种 机制去实现master的决策,redis并没有提供自动master选举功能,而是需要借助一个哨兵来进行监控
# 8. 什么是哨兵
顾名思义,哨兵的作用就是监控Redis系统的运行状况,它的功能包括两个
1. 监控master和slave是否正常运行
2. master出现故障时自动将slave数据库升级为master
哨兵是一个独立的进程,使用哨兵后的架构图

为了解决master选举问题,又引出了一个单点问题,也就是哨兵的可用性如何解决。
在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳定。
此时哨兵不仅会监控master和slave,同时还会互相监控;这种方式称为哨兵集群,
哨兵集群需要解决故障发现、和master决策的协商机制问题

# 9. Sentinel之间的相互感知
sentinel节点之间会因为共同监视同一个master从而产生了关联,一个新加入的sentinel节点需要和
其他监视相同 master节点的sentinel相互感知,首先
1. 需要相互感知的sentinel都向他们共同监视的master节点订阅channel:sentinel:hello
2. 新加入的sentinel节点向这个channel发布一条消息,包含自己本身的信息,
这样订阅了这个channel的sentinel就可以发现这个新的sentinel
3. 新加入得sentinel和其他sentinel节点建立长连接
# 10. master的故障发现
sentinel节点会定期向master节点发送心跳包来判断存活状态,
一旦master节点没有正确响应,sentinel会把 master设置为“主观不可用状态”,
然后它会把“主观不可用”发送给其他所有的sentinel节点去确认,
当确认的 sentinel节点数大于>quorum时,则会认为master是“客观不可用”,
接着就开始进入选举新的master流程;但是 这里又会遇到一个问题,就是sentinel中,本身是一个集群,
如果多个节点同时发现master节点达到客观不可用状态,那谁来决策选择哪个节点作为maste呢?
这个时候就需要从sentinel集群中选择一个leader来做决策。
而这里用到了一致性算法Raft算法、它和Paxos算法类似,都是分布式一致性算法。但是它比Paxos算法要更容易理解;
Raft和Paxos算法一样,也是基于投票算法,只要保证过半数节点通过提议即可;
# 11. 配置实现
通过在这个配置的基础上增加哨兵机制。在其中任意一台服务器上创建一个sentinel.conf文件,文件内容
sentinel monitor name ip port quorum
其中name表示要监控的master的名字,这个名字是自己定义。 ip和port表示master的ip和端口号。
最后一个1表示最低 通过票数,也就是说至少需要几个哨兵节点统一才可以。
port 6040
sentinel monitor mymaster 192.168.11.131 6379 1
--表示如果5s内mymaster没响应,就认为SDOWN
sentinel down-after-milliseconds mymaster 5000
--表示如果15秒后,mysater仍没活过来,则启动failover,
从剩下的 slave中选一个升级为master
sentinel failover-timeout mymaster 15000
# 12. 两种方式启动哨兵
redis-sentinel sentinel.conf
redis-server /path/to/sentinel.conf --sentinel
哨兵监控一个系统时,只需要配置监控master即可,哨兵会自动发现所有slave;
这时候,我们把master关闭,等待指定时间后(默认是30秒),会自动进行切换
# 13. Redis 集群 Redis-Cluster
即使是使用哨兵,此时的Redis集群的每个数据库依然存有集群中的所有数据,
从而导致集群的总数据存储量受限于可用存储内存最小的节点,形成了木桶效应。
而因为Redis是基于内存存储的,所以这一个问题在redis中就显得尤为突出了
在redis3.0之前,我们是通过在客户端去做的分片,通过hash环的方式对key进行分片存储。
分片虽然能够解决各个节点的存储压力,但是导致维护成本高、增加、移除节点比较繁琐。
因此在redis3.0以后的版本最大的一个好处 就是支持集群功能,集群的特点在于拥有和单机实例一样的性能,
同时在网络分区以后能够提供一定的可访问性以 及对主数据库故障恢复的支持。
哨兵和集群是两个独立的功能,当不需要对数据进行分片使用哨兵就够了,如果要进行水平扩容,集群是一个比较好的方式
# 14. 拓扑结构
一个Redis Cluster由多个Redis节点构成。不同节点组服务的数据没有交集,
也就是每个一节点组对应数据 sharding的一个分片。节点组内部分为主备两类节点,
对应master和slave节点。两者数据准实时一致,通过异步化的主备复制机制来保证。
一个节点组有且只有一个master节点,同时可以有0到多个slave节点,
在这个节点组中只有master节点对用户提供些服务,读服务可以由master或者slave提供
redis-cluster是基于gossip协议实现的无中心化节点的集群,因为去中心化的架构不存在统一的配置中心,
各个节点对整个集群状态的认知来自于节点之间的信息交互。在Redis Cluster,这个信息交互是通过Redis Cluster Bus来完成的
# 15. Redis的数据分区
分布式数据库首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,
每个节点负责整个数据的一个子集, Redis Cluster 采用 哈希分区规则 ,采用 虚拟槽分区。
虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,
整数定义为槽(slot)。比如Redis Cluster槽的范围是0 ~ 16383。槽是集群内数据管理和迁移的基本单位。
采用大范围的槽的主要目的是为了方便数据的拆分和集群的扩展,每个节点负责一定数量的槽。
计算公式:slot = CRC16(key)%16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。

# 16. HashTags
通过分片手段,可以将数据合理的划分到不同的节点上,这本来是一件好事。但是有的时候,
我们希望对相关联的业务以原子方式进行操作。
要求key尽可能的分散在不同机器,又要求某些相关联的key分配到相同机器。
这个也是在面试的时候会容易被问到的内容。怎么解决呢?
从前面的分析中我们了解到,分片其实就是一个hash的过程,对key做hash取模然后划分到不同的机器上。
所以为了解决这个问题,我们需要考虑如何让相关联的key得到的hash值都相同呢?如果key全部相同是不现实的,所以怎么解决呢?
在redis中引入了HashTag的概念,可以使得数据分布算法可以根据key的某一个部分进行计算,
然后让相关的key落到同一个数据分片
举个简单的例子,加入对于用户的信息进行存储, user:user1:id、user:user1:name
那么通过hashtag 的方式,
user:{user1}:id、
user:{user1}.name;
表示 当一个key包含 {} 的时候,就不对整个key做hash,而仅对 {} 包括的字符串做hash。
# 17. 重定向客户端
Redis Cluster并不会代理查询,那么如果客户端访问了一个key并不存在的节点,这个节点是怎么处理的呢?
比如 我想获取key为msg的值,msg计算出来的槽编号为254,当前节点正好不负责编号为254的槽,
那么就会返回客户 端下面信息:
-MOVED 254 127.0.0.1:6381
表示客户端想要的254槽由运行在IP为127.0.0.1,端口为6381的Master实例服务。
如果根据key计算得出的槽恰好 由当前节点负责,则当前节点会立即返回结果
# 18. 分片迁移
在一个稳定的Redis cluster下,每一个slot对应的节点是确定的,但是在某些情况下,节点和分片对应的关系会发生变更
1. 新加入master节点
2. 某个节点宕机
也就是说当动态添加或减少node节点时,需要将16384个槽做个再分配,槽中的键值也要迁移。
当然,这一过程, 在目前实现中,还处于半自动状态,需要人工介入。
新增一个主节点
新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上。大致就会 变成这样:
节点A覆盖1365-5460
节点B覆盖6827-10922
节点C覆盖12288-16383
节点D覆盖0-1364,5461-6826,10923-12287
删除一个主节点
先将节点的数据移动到其他节点上,然后才能执行删除
# 19. 槽迁移的过程
槽迁移的过程中有一个不稳定状态,这个不稳定状态会有一些规则,这些规则定义客户端的行为,
从而使得Redis Cluster不必宕机的情况下可以执行槽的迁移。下面这张图描述了我们迁移编号为1、2、3的槽的过程中,
他们在 MasterA节点和MasterB节点中的状态。

简单的工作流程
1. 向MasterB发送状态变更命令,把Master B对应的slot状态设置为IMPORTING
2. 向MasterA发送状态变更命令,将Master对应的slot状态设置为MIGRATING
当MasterA的状态设置为MIGRANTING后,表示对应的slot正在迁移,
为了保证slot数据的一致性,MasterA此时 对于slot内部数据提供读写服务的行为和通常状态下是有区别的.
MIGRATING状态
1. 如果客户端访问的Key还没有迁移出去,则正常处理这个key
2. 如果key已经迁移或者根本就不存在这个key,则回复客户端ASK信息让它跳转到MasterB去执行
IMPORTING状态
当MasterB的状态设置为IMPORTING后,表示对应的slot正在向MasterB迁入,
及时Master仍然能对外提供该slot 的读写服务,但和通常状态下也是有区别的
当来自客户端的正常访问不是从ASK跳转过来的,说明客户端还不知道迁移正在进行,
很有可能操作了一个目前 还没迁移完成的并且还存在于MasterA上的key,
如果此时这个key在A上已经被修改了,那么B和A的修改则会发生 冲突。
所以对于MasterB上的slot上的所有非ASK跳转过来的操作,MasterB都不会处理,
而是通过MOVED 命令让客户端跳转到MasterA上去执行
这样的状态控制保证了同一个key在迁移之前总是在源节点上执行,
迁移后总是在目标节点上执行,防止出现两边 同时写导致的冲突问题。
而且迁移过程中新增的key一定会在目标节点上执行,源节点也不会新增key,
是的整个迁移过程既能对外正常提供服务,又能在一定的时间点完成slot的迁移。
# 20. Redis的数据持久化
Redis支持两种方式的持久化,一种是RDB方式、另一种是AOF(append-only-file)方式。
前者会根据指定的规则“定时”将内存中的数据存储在硬盘上,而后者在每次执行命令后将命令本身记录下来。
两种持久化方式可以单独使用其中一种,也可以将这两种方式结合使用。
# 21. RDB方式
当符合一定条件时,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,
等到持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,
那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
Redis会在以下几种情况下对数据进行快照
1. 根据配置规则进行自动快照
2. 用户执行SAVE或者GBSAVE命令
3. 执行FLUSHALL命令
4. 执行复制(replication)时
a. 根据配置规则进行自动快照
Redis允许用户自定义快照条件,当符合快照条件时,Redis会自动执行快照操作。
快照的条件可以由用户在配置文 件中配置。配置格式如下
save
第一个参数是时间窗口,第二个是键的个数,也就是说,在第一个时间参数配置范围内被更改的键的个数大于后面 的changes时,
即符合快照条件。redis默认配置了三个规则
save 900 1
save 300 10
save 60 10000
每条快照规则占一行,每条规则之间是“或”的关系。 在900秒(15分)内有一个以上的键被更改则进行快照。
b.用户执行SAVE或BGSAVE命令
除了让Redis自动进行快照以外,当我们对服务进行重启或者服务器迁移我们需要人工去干预备份。redis提供了两 条命令来完成这个任务
1. save命令
当执行save命令时,Redis同步做快照操作,在快照执行过程中会阻塞所有来自客户端的请求。
当redis内存中的数据较多时,通过该命令将导致Redis较长时间的不响应。所以不建议在生产环境上使用这个命令,而是推荐使用 bgsave命令
2. bgsave命令
bgsave命令可以在后台异步地进行快照操作,快照的同时服务器还可以继续响应来自客户端的请求。执行BGSAVE后,
Redis会立即返回ok表示开始执行快照操作。
通过
LASTSAVE
命令可以获取近一次成功执行快照的时间; (自动快照采用的是异步快照操作)
c.执行FLUSHALL命令
清除redis在内存中的所有数据。执行该命令后,只要redis中配置的快照规则不为空,也就是save 的规则存在。redis就会执行一次快照操作。不管规则是什么样的都会执行。如果没有定义快照规则,就不会执行快照操作
d.执行复制时
该操作主要是在主从模式下,redis会在复制初始化时进行自动快照。
# 22. 开启AOF
默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数启用,在redis.conf 中找到
appendonly yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令后,Redis就会将该命令写入硬盘中的AOF文件。
AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是apendonly.aof.
可以在redis.conf 中的属性 appendfilename appendonlyh.aof修改。
我们会发现AOF文件的内容正是Redis发送的原始通信协议的内容,从内容中我们发现Redis只记录了3 条命令。
然后这时有一个问题是前面2条命令其实是冗余的,因为这两条的执行结果都会被第三条命令覆盖。
随着执行的命令越来越多,AOF文件的大小也会越来越大,其实内存中实际的数据可能没有多少,
那这样就会造成磁盘空间以及redis数据还原的过程比较长的问题。
因此我们希望Redis可以自动优化 AOF文件。
而实际上Redis也考虑到了,可以配置一个条件,每当达到一定条件时Redis就会自动重写AOF文件,
这个条件的配置 auto-aof-rewritepercentage 100 auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 表示的是当目前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会
再次进行重写,如果之前没有重写过,则以启动时AOF文件大小为依据
auto-aof-rewrite-min-size 表示限制了允许重写的小AOF文件大小,通常在AOF文件很小的情况下即使其中有很多冗余的命令我们也并不太关心。
另外,还可以通过
BGREWRITEAOF
命令手动执行AOF,执行完以后冗余的命令已经被删除了
在启动时,Redis会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,载入的速度相对于RDB会慢一些
AOF的重写原理
Redis 可以在 AOF文件体积变得过大时,自动地在后台对 AOF进行重写: 重写后的新 AOF 文件包含了恢复当前 数据集所需的小命令集合。
重写的流程是这样,主进程会fork一个子进程出来进行AOF重写,这个重写过程并不是基于原有的aof文件来做的,而是有点类似于快照的方式,全量遍历内存中的数据,
然后逐个序列到aof文件中。在fork子进程这个过程中,服务端仍然可以对外提供服务,那这个时候重写的aof文件的数据和redis内存数据不一致了怎么办?
这个过程中,主进程的数据更新操作,会缓存到aof_rewrite_buf中,也就是单独开辟一块缓存来存储重写期间
收到的命令,当子进程重写完以后再把缓存中的数据追加到新的aof文件。 当所有的数据全部追加到新的aof文件中后,
把新的aof文件重命名为,此后所有的操作都会被写入新的aof文件。
如果在rewrite过程中出现故障,不会影响原来aof文件的正常工作,
只有当rewrite完成后才会切换文件。因此这个rewrite过程是比较可靠的。
# 23. 缓存雪崩
缓存雪崩是指设置缓存时采用了相同的过期时间,导致缓存在某一个时刻同时失效,或者缓存服务器宕机宕机导致
缓存全面失效,请求全部转发到了DB层面,DB由于瞬间压力增大而导致崩溃。缓存失效导致的雪崩效应对底层系 统的冲击是很大的。
解决方式
1. 对缓存的访问,如果发现从缓存中取不到值,那么通过加锁或者队列的方式保证缓存的单进程操作,
从而避免失效时并发请求全部落到底层的存储系统上;但是这种方式会带来性能上的损耗
2. 将缓存失效的时间分散,降低每一个缓存过期时间的重复率
3. 如果是因为缓存服务器故障导致的问题,一方面需要保证缓存服务器的高可用、另一方面,应用程序中可以采 用多级缓存
# 24. 缓存穿透
缓存穿透是指查询一个根本不存在的数据,缓存和数据源都不会命中。出于容错的考虑,如果从数据层查不到数据 则不写入缓存,
即数据源返回值为 null 时,不缓存 null。缓存穿透问题可能会使后端数据源负载加大,由于很多后端数据源不具备高并发性,甚至可能造成后端数据源宕掉
解决方式
1. 如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,
这种办法最简单粗暴。比如,”key” , “&&”。 在返回这个&&值的时候,我们的应用就可以认为这是不存在的key,
那我们的应用就可以决定是否继续等待继续访问,还是放弃掉这次操作。如果继续等待访问,过一个时间轮询点后,再次请求这个key,
如果取到的值不再是 &&,则可以认为这时候key有值了,从而避免了透传到数据库,从而把大量的类似请求挡在了缓存之中。
2. 根据缓存数据Key的设计规则,将不符合规则的key进行过滤采用布隆过滤器,
将所有可能存在的数据哈希到一个足够大的BitSet中,不存在的数据将会被拦截掉,从而避免了对底层存储系统的查询压力
# 25. 布隆过滤器

# 26. Jedis 实现分布锁
/**
* NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;
若key已经存在,则不做任何操作;
* PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定
* @param lockKey
* @param requestId
* @param expireTime
* @return
*/
public boolean tryGetDistributedLock(String lockKey, String requestId,
int expireTime) {
Jedis jedis = null;
jedis = JedisConnectionUtils.getJedis();
String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return true;
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 27. Redisson实现分布式锁
Config config=new Config();
config.useSingleServer().setAddress("redis://192.168.11.152:6379");
RedissonClient redissonClient=Redisson.create(config);
RLock rLock=redissonClient.getLock("updateOrder");
//最多等待100秒、上锁10s以后自动解锁
if(rLock.tryLock(100,10,TimeUnit.SECONDS)){
System.out.println("获取锁成功");
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 28. Redis是单进程?性能为什么这么快
官方的解释是,CPU并不是Redis的瓶颈所在,Redis的瓶颈主要在机器的内存和网络的带宽。
1. Redis是纯内存数据库,所以读取速度快。
2. 再说一下IO,Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。
3. Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
4. 另外,数据结构也帮了不少忙,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,
如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
5. 还有一点,Redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。
# 29. Redis内存回收策略
Redis中提供了多种内存回收策略,当内存容量不足时,为了保证程序的运行,这时就不得不淘汰内存中的一些对象,
释放这些对象占用的空间,那么选择淘汰哪些对象呢?
其中,默认的策略为noeviction策略,当内存使用达到阈值的时候,所有引起申请内存的命令会报错.
allkeys-lru:从数据集(server.db[i].dict)中挑选近少使用的数据淘汰
适合的场景: 如果我们的应用对缓存的访问都是相对热点数据,那么可以选择这个策略
allkeys-random:随机移除某个key。
适合的场景:如果我们的应用对于缓存key的访问概率相等,则可以使用这个策略
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选近少使用的数据淘汰。
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
适合场景:这种策略使得我们可以向Redis提示哪些key更适合被淘汰,我们可以自己控制
# 30. 数据类型
1. String String类型通过 int、SDS(simple dynamic string)作为结构存储,int用来存放整型数据,sds存放字 节/字符串和浮点型数据
2. List
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获得列表的某一个片段。
列表类型内部使用quicklist双向链表,每个节点都是一个ziplist。向列表两端添加元素的时间复杂度为O(1),
获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是很快的
3. Hash
hash提供两种结构来存储,一种是hashtable、另一种是ziplist,数据量小的时候用ziplist.
4. Set
集合类型中,每个元素都是不同的,也就是不能有重复数据,同时集合类型中的数据是无序的。一个集合类型键可以存储至多232-1个 。
集合类型和列表类型的最大的区别是有序性和唯一性 集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在。
由于集合类型在redis内部是使用的值为空的散列表(hash table),所以这些操作的时间复杂度都是O(1).
Set在的底层数据结构以intset或者hashtable来存储。当set中只包含整数型的元素时,采用intset来存储,否则, 采用hashtable存储,
但是对于set来说,该hashtable的value值用于为NULL。通过key来存储元素。
5. Sorted Set
在集合类型的基础上,有序集合类型为集合中的每个元素都关联了一个分数,这使得我们不仅可以完成插入、删除 和判断元素是否存在等集合类型支持的操作,
还能获得分数最高(或最低)的前N个元素、获得指定分数范围内的元 素等与分数有关的操作。虽然集合中每个元素都是不同的,但是他们的分数却可以相同
# 31. 过期删除的原理
消极方法(passive way)
在主键被访问时如果发现它已经失效,那么就删除它
积极方法(active way)
周期性地从设置了失效时间的主键中选择一部分失效的主键删除
对于那些从未被查询的key,即便它们已经过期,被动方式也无法清除。
因此Redis会周期性地随机测试一些key, 已过期的key将会被删掉。Redis每秒会进行10次操作
具体的流程:
1. 随机测试 20 个带有timeout信息的key;
2. 删除其中已经过期的key;
3. 如果超过25%的key被删除,则重复执行步骤1;