redis单机数据库

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

1
2
3
4
5
6
7
8
9
10
struct redisServer{

//服务器初始化数据库数量
int dbnum;

//保存数据库的数组
redisDb *db;
...
...
};

redisDb的结构如下:

1
2
3
4
5
6
7
typedef struct redisDb{
//数据库键空间,保存着数据库中的所有键值对
dict *dict;

...
...
}redisDb;

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

  1. 键空间的键也就是数据库的键,每一个键都是一个字符串对象
  2. 键空间的值也就是数据库的值,每个值可以是字符串对象、列表对象、哈希对象等redis中的任何对象。

键的生存时间

通过expire和pexpire命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间:

1
2
3
4
5
6
7
8
redis> set key 9
OK

redis> expire key 5 //5秒后过期
(integer) 1

redis> get key //5秒后重新获取key时,会出现错误
(nil)

expireat和pexpireat命令用来设置在某个时间戳后,键超时:

1
2
3
4
5
6
redis> set key value
OK

//设置在特定时间戳后过期
redis> expireat key 1377257300
(integer) 1

可以使用persist来取消键的过期时间:

1
2
3
4
5
6
7
8
9
10
11
redis> expire key 5
OK

redis> TTL key
(integer) ****

redis> persist key
(integer) 1

redis> TTL key
(integer) -1

redis使用expires字典来保存数据库中键的过期时间,对于过期键,需要删除,否则的话会占用内存。对于过期键的删除,有三种可能的策略:

  1. 定时删除:在设置键的过期时间时,创建一个定时器,让定时器在键的过期时间来临时,立即执行删除操作。该策略对内存是最友好的,但是对CPU最不友好
  2. 惰性删除:放任键过期不管,但访问到键时,才对键执行删除操作,如果过期的键没有被访问到,那么键就直接存在内存。该策略对CPU最友好,但是对内存不友好
  3. 定期删除:每隔一段时间,程序就对数据库进行一次检查,删除过期的键。至于要删除多少,以及检查多少个数据库,则由算法决定
    redis采用惰性和定期删除两种策略,通过配合使用这两种策略,服务器可以很好的在合理使用CPU时间和避免浪费内存空间之间取得平衡。

redis服务器采用reactor的模型,是一个事件驱动程序,在redis服务器中,有两种事件:文件事件和时间事件。文件事件就是所建立的socket而时间事件就是redis自己需要的,用来执行一些定时操作。
redis采用IO复用的模式来处理文件事件,对于时间事件,由于目前redis的时间事件很少,所以redis采用链表的形式来保存所有的时间事件。每次遍历链表,执行所有超时的时间事件。

因为服务器中同时存在文件事件和时间事件两种类型,所以服务器必须对这两种事件进行调度,决定何时应该处理文件事件和时间事件,以及花多少时间来处理他们。redis会遍历时间事件,获得最近的定时事件的时间,如果定时事件已经到达,那么时间为0,redis以该时间来决定监听IO复用的超时时间。(其实这里如果时间事件是平凡执行的,那么redis会在时间事件上busy,导致文件事件饿死,但是由于时间事件是redis自己加的,不对外开放,所以不会存在恶意时间事件)。