1、简述

(1)关于Redis键的过期策略,首先要了解两种时间的区别,生存时间和过期时间;

      • 生存时间:一段时长,如30秒、6000毫秒,设置键的生存时间就是设置这个键可以存在多长时间,命令有两个 expire(秒)、pexpire(毫秒)(可以参考 Redis(四)--- Redis命令参考)。
      • 过期时间:一个时间点,unix时间戳,设置键的过期时间就是设置键在之后的某个时间点过期,命令两个expreat(秒时间戳)、pexpireat(毫秒时间戳)。
      • 无论是过期时间或生存时间,在底层存储都会转换为unix时间戳,由于过期就是设置的unix时间错,所以我们通常把两种时间都叫做过期时间。 

(2)当内存使用达到Redis限制的上限时,就会触发内存淘汰机制。 

(3)Redis的发布与订阅功能可以作为简单的消息队列使用。

(4)当多条命令需要原子性执行时,可以开启Redis的事物命令进行执行。

2、过期策略

通过上一章(Redis(五)--- Redis的持久化RDB和AOF)第一节可以知道,Reids键的过期时间都是存在数据库结果中的过期字典中的,那么一个键过期了,什么时候回进行删除呢?有三种策略。

(1)定时删除:在设置过期时间的同时,设置一个定时器,定时器的执行时间就是过期的时间点。

      • 优点:对内存最友好,过期的键会以最快的被删除,释放内存。
      • 缺点:对CPU时间最不友好,在大量键设置过期时间时,会创建大量的定时器,执行浪费CPU时间。

(2)惰性删除:不管键是否过期,只有每次取值的时候,才检查是否过期,过期就删除。

      • 优点:对CPU时间最友好,取值时检查,只对当前键操作,不影响其他。
      • 缺点:队内存不友好,可能会存在大量过期的未被使用的键值没有删除,无用数据占用了大量内存。

(3)定期删除:每隔一段时间,程序对数据库进行一次检查,过期的就删除。

      • 优点:前两种方案的折中,通过减少执行频率来减少对CPU时间的影响,通过定期删除减少了对内存的浪费。
      • 缺点:执行频率需要掌握好,不然太频繁则退化成定时删除,太少则退化成惰性删除。

Redis采用的是惰性删除定期删除两种策略。

持久化和复制对过期键的处理

(1)RDB持久化:

主服务器:RDB文件无论是生成或载入,都会对过期键进行检查;生成时,过期键不写入;载入时,过期键会忽略。

从服务器:载入时,不会检查是否过期,数据都会载入。

(2)AOF持久化:AOF文件写入时,键过期未删除,不影响;键过期已删除,则在AOF文件后追加DEL命令。

(3)AOF重写:AOF重写过程中会进行检查,过且的键忽略。

(4)复制:主从模式下,由主服务器进行删除过期键,并显示的向从服务器发送DEL命令;从服务器自身不具备删除过期键值行为。 

3、内存淘汰机制

当Redis的内存使用达到设置的内存上限时就会触发内存淘汰机制,按照特定的淘汰算法进行数据清理,释放内存。

具体的内存淘汰算法有一以下几种:

(1)noeviction:不淘汰,内存不足时, 新写入会报错。

(2)allkeys-lru:LRU,内存不足时,淘汰最近最少使用的key。

(3)allkeys-random:随机,内存不足时,在所有key中随机选择一个key淘汰。

(4)volatile-lru:过期时间内LRU,内存不足时,在设置了过期时间的key中,淘汰最近最少使用的key。

(5)volatile-random:过期时间内随机,内存不足时,在设置了过期时间的key中,随机选择一个key淘汰。

(6)volatile-ttl:更早过期时间,内存不足时,在设置了过期时间的key中,选择有更早过期时间的key淘汰。

Redis默认使用的是LRU算法,需要注意的是LRU并不是多有的keys进行LRU,而是在所有的key中随机选择3个key,在这3个key中进行LRU算法选择;3个这个个数可以在redis配置文件中进行配置maxmeory-samples选项。

在大规模并发的情况下,我们可以使用集群方式进行内存的扩充。

4、消息

这里所说到的消息是指Redis的发布与订阅功能,在笔者的项目中,就实际用到了这个功能,具体是在分布式session中用到的,作用是减少的共享sesssion访问redis的次数,用发布与订阅模式通知session的更新、删除等;具体思路参考了J2Cache框架,J2Cache是开源框架,作者红薯等;当然笔者的实现太简单了,没有那么多功能;还有一次用到的就是作为简单的消息队列使用的,在一个小的独立模块中,没有使用消息队列,用此功能代替。

Redis的发布订阅功能由publish、subscribe、psubscribe等命令组成,客户端可以订阅一个和多个频道,频道的所有订阅者都可以收到其他客户端发送的消息,说道这里和微信的公众号推送好像啊。

Redis(六)--- Redis过期策略、内存淘汰机制、消息及事物-LMLPHP

客户端不单单可以订阅某个频道,还可以以通配方式订阅多个频道,如图:

Redis(六)--- Redis过期策略、内存淘汰机制、消息及事物-LMLPHP

订阅频道的底层存储结构

  (1)常规订阅模式

Redis将所有频道的订阅关系都存储到底层的一个字典里(貌似redis的底层数据都是放在字典里,key-value数据,设置的过期时间),字典的键是被订阅的频道,字典的值是一个链表,链表里记录了所有订阅者。

Redis(六)--- Redis过期策略、内存淘汰机制、消息及事物-LMLPHP

      • 当有订阅者订阅频道时,订阅者就会放到订阅频道对应的链表的表尾,如果是新频道就创建一个空链表后再添加。
      • 当有订阅者退订频道时,就会删除对应链表上的节点。

(2)通配订阅模式

在Redis的书中把此种订阅称为模式的订阅,而笔者本身为了便于记忆和理解更习惯称之为通配订阅模式;通配模式订阅的数据都存在底层数据的链表中(这次不是字典了),链表的每个节点记录着一个pubsubPattern结构,结构里client属性记录了客户端,而pattern属性记录了订阅频道的通配符,如图

Redis(六)--- Redis过期策略、内存淘汰机制、消息及事物-LMLPHP

      • 当客户端以通配符模式订阅时,链表便会增加一个pubsubPattern结构存储客户端和通配符结构,然后将其添加到链表的表尾。
      • 当有客户端退订时,将会在链表中查找到对应的通配符结构,并删除对应的pubsubPattern结构。

了解了底层的存储结构就可以明白消息的发送就是轮训链表的查找匹配的订阅者的过程。

关于发布订阅命令的用法请参考 《Redis(四)--- Redis命令参考

5、事物

Redis通过multi、exec、watch等命令实现事务功能;事物是将多个命令打包,然后一次性的顺序执行。

Redis事物也ACID性质:

(1)原子性(Atomicity):事物内的命令,要么都执行,要么都不执行;

(2)一致性(Consistency):事物执行前后,数据库是一致的,不会包含非法或无效数据,不论事物是否执行成功。

(3)隔离性(Isolation):多个事物并发执行,不会相互影响;并行和串行结果一致,因为redis是单线程的方式执行事物的。

(4)耐久性(Durability):当一个事物执行完毕后,结果将被保存到永久的存储介质中,即便服务器停机,事物之后的数据也不会丢失。

事物的实现

事物以MULTI命令开始,以EXEC命令开始执行命令,在两个命令中间可以书写多条命令,这些命令会放入事物队列等待执行,这个对列是放在客户端底层结构的multiState数据结构中。

 1 typedef struct multiState {
 2   // 事务队列,FIFO顺序
 3   multiCmd *commands;
 4   // 已入队命令计数
 5   int count;
 6 } multiState;
 7
 8
 9 typedef struct multiCmd {
10   // 参数
11   robj **argv;
12   // 参数数量
13   int argc;
14   // 命令指针
15   struct redisCommand *cmd;
16 } multiCmd;

Redis(六)--- Redis过期策略、内存淘汰机制、消息及事物-LMLPHP

    • 事物队列,具有先进先出特性,先放入的命令,事物开始后先执行。
    • 事物队列是一个multiCmd类型的数组,数组中的每个元素都保存一个已入队的命令。
    • 事物开始执行,服务器会遍历这个客户端的事务队列,执行队列中的所有命令,然后将结果返回给客户端。
    • watch命令可以监视事物内的命令操作的键,如果键在事物内时有其他客户端修改了,那么事物将会被拒绝执行;watch命令在multi命令之前执行。

6、总结

    • Redis的过期策略和内存淘汰机制是两种不同的方式,注意不要混淆
    • 在内存淘汰期间并不会影响过期删除处理,过期策略主要用于处理内存中过且的数据。
    • 发布订阅功能可以作为简单的消息队列使用。
    • 事物使用是安全的,服务器会根据客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务,如果打开了,则证明至少有一个键被修改了,则会拒绝执行事物。

参考:

《Redis设计与实现》黄健宏著,网上对Redis的详解等

此博客为笔者使用redis很久之后,参考网络上各类文章总结性书写,原创手打,如有错误欢迎指正。 

07-31 03:50