谈谈Redis你还不知道的

 学好Redis,走遍天下都不怕

  • 服务器保存数据库的方法
  • 客户端切换数据库的方法
  • 数据库保存键值对的方法
  • 数据库的增加,删除,更新,查看操作
  • 服务器保存键的过期时间的方法
  • 服务器自动删除过期键的方法

1.服务器保存数据库的方

struct redisServer{

 //...

 //一个数组,保存所有的数据库

  redisDb *db;

  //..

}

Redis服务器将所有的数据库都保存在服务器状态 redis.h/redisServer结构的db数组中,db数组的每个项都是一个redis.h/redisDb结构,每个redisDb结构代表一个数据库

2.客户端切换数据库的方法

每个Redis客户端都有自己的目标数据库

默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行select命令来切换目标数据库

代码示例:

在服务器内部,客户端状态redisClient结构的db属性记录了客户端当前的目标数据库

typeof struct redisClient{

//..

//记录客户端当前正在使用的数据库

redisDb *db;

//..

}redisClient;

redisClient.db指针指向redisServer.db数组的其中一个元素,而被指向的元素就是客户端的目标数据库。(仔细品。。)

目标数据库为1号数据库时,客户端的状态和服务器状态之间的关系:

当执行命令select 2,客户端的状态和服务器状态之间的关系更新为:

通过修改redisClient.db指针,让它指向服务器中的不同数据库,从而实现切换目标数据库的功能——这就是select命令的实现原理

3.数据库保存键值对的方法

Redis是一个键值对数据库服务器,服务器中的每个数据都有一个redis.h/redisDb结构表示

redisDb结构的dict字典保存了数据库中的所有键值对,这个字典就是键空间

typedef struct redisDb{

//..

dict * dict;

//..

}redisDb;

键空间和用户所见的数据库是直接对应的:

键空间中的键就是数据库的键,每个键都是字符串对象

键空间中的值就是数据库的值,每个值可以是字符串对象,列表对象,哈希表对象,集合对象和有序集合对象的任意一种

如图:

4.数据库的增加,删除,更新,查看操作

添加一个新键值对到数据库,实际上就是将一个新键值对添加到键空间字典里面

删除数据库的一个键,实际上就是在键空间里面删除键所对应的键值对对象

对一个数据库键进行更新,实际上就是对键空间里面键所对应的值对象进行更新,值对象类型不同,更新的具体方法也会有不同。

对一个数据库键进行取值,实际上就是对键空间里面取出键所对应的值对象,值对象类型不同,取值的具体方法也会有不同。

除了上面列出的添加,删除,更新,取值操作之外,还有:

用于清空整个是数据库的flushdb命令,就是通过删除键空间中的所有键值对来实现的

用于随机返回数据库中的某个键的randomkey命令,就是通过在键空间中随机返回一个键来实现的

用于返回数据库数量的dbsize命令,就是通过返回键空间中包含的键值对数量来实现的

5.读写键空间时的维护操作

当使用Redis命令对数据库进行读写时,服务器不仅会对键空间执行指定的读写操作,还会执行一些额外的维护操作,比如:

读取一个键之后(读操作和写操作都要对键进行读取),服务器会根据键是否存在来更新服务器的键空间 命中次数(hit)或不命中次数(miss),

6.服务器保存键的过期时间的方法

1)保存过期时间

redisDb结构的expires字典保存了数据库中所有键的过期时间,这个字典也即过期字典

过期字典的 键 是一个指针,指向键空间中的某个键对象

过期字典的 值 是一个long类型的整数,保存了键所指向的数据库键的过期时间(一个毫秒精度的时间戳)

typedef struct redisDb{

//..

//过期字典,保存键的过期时间

dict *expires;

//..

}redisDb;

键空间保存了数据库中的所有键值对,而过期字典中保存了数据库键的过期时间

2)设置过期时间

expire命令 以秒为单位 设置键的过期时间(生存时间)

pexpire命令 以毫秒为单位 设置键的过期时间

setex命令  设置一个字符串键的同时为键设置过期时间(只能用于字符串键)

expireat命令     以秒为单位 设置键的过期时间   (过期时间是一个unix时间戳)

 

pexpireat命令   以毫秒为单位 设置键的过期时间  (过期时间是一个unix时间戳)

实际上,expire,pexpire, expireat,三个命令都是使用pexpireat命令来实现的, 无论客户端执行的是上述四个命令的哪一个,经过转换之后,最终的执行效果和执行pexpireat命令一样。

3)移除过期时间

persist命令 可以移除一个键的过期时间,它就是pexpireat命令的反操作

persist命令在过期字典中查找给定的键,并解除键和值在过期字典中的关联

 

4)计算并返回剩余生存时间

ttl命令     以秒为单位返回键的剩余生存时间

pttl命令    以毫秒为单位返回键的剩余生存时间

 

7.服务器自动删除过期键的方法

 

如果一个键过期了,那么它什么时候会被删除呐?

这里有三种不同的删除策略:

 

定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。

惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。

定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删多少过期键,要检查多少个数据库,则有算法决定。

 

1)定时删除

定时删除对内存是友好的,通过使用定时器,定时删除策略可以保证过期键会尽快的被删除,并释放过期键所占用的内存。

 

他对cpu时间是不友好的,在过期键比较多的情况下,删除过期键这一行为可能会占用相对一部分的cpu时间,在内促不紧张但cpu时间非常紧张的情况下,将cpu时间用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。

除此之外,创建一个定时器需要用到Redis服务器中的时间事件,而当前时间事件的实现方式——无序链表,查找一个事件的时间复杂度为O(n),并不高效。

 

因此,让服务器创建大量的定时器,从而实现定时删除策略,在现阶段并不现实。

2)惰性删除

惰性删除对cpu时间是友好的,程序只会在取出键时才会对键进行过期检查,并且删除的键仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何的cpu时间。

 

它对内存是最不友好的,如果一个键已经过期,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,它所占用的内存就不会释放。

3)定期删除

定时删除和惰性删除这两种方式在单一使用时都有明显的缺陷

定时删除占用太多cpu时间,影响服务器的响应时间和吞吐量

惰性删除浪费太多内存,有内存泄露的危险

 

定期删除策略是前两种策略的一种整合和折中:

它每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对cpu时间的影响

通过定期删除过期键,有效减少了因为过期键而带来的内存浪费

 

定期删除策略的难点是确定删除操作执行的时长和频率:

如果删除操作执行的太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,以致于将C P U时间过多的消耗在删除过期键上面。

如果删除操作执行的太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况。

 

因此,如果采用定期删除策略的话,服务器必须根据情况,合理的设置删除操作的执行时长和执行频率。

Redis的过期键删除策略

Redis服务器实际使用的是惰性删除和定期删除两种策略:提供两种删除策略的配合使用,服务器可以很好的使用cpu时间和避免浪费内存空间之间取得平衡。

 

过期键的惰性删除由db.c/expireIfNeeded函数实现,所有读写数据库的Redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查:

 

过期键的定期删除由redis.c/activeExpireCycle函数实现,每当Redis的服务器周期性操作redis.c/serverCron函数执行时, activeExpireCycle函数就会被调用,他在规定的时间内,分多次遍历服务器中的各个数据库,从数据库的expire字典中随机检查一部分键的过期时间,并删除其中的过期键。

整个过程用伪代码描述如下:

 

 

activeExpireCycle函数的工作模式可以总结为:

 

原文地址:https://www.cnblogs.com/yunhemeihe/p/12823731.html