0%

redis设计与实现-单机数据库的实现

1 服务器中的数据库

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

1
2
3
4
5
6
7
8
struct redisServer {
// ...
// 一个数组,保存着服务器中的所有数据库.
redisDb *db;
// 总配置的数据库的数量.
int dbnum;
// ...
};

dbnum属性的值由服务器配置的database选项决定,默认情况下,该选项的值为16。

2 切换数据库

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

示例:

1
2
# 切换到数据库2.
redis> select 2

其中redisClient结构中的db属性记录了当前客户端的目标数据库,该属性是一个指向redisDb结构的指针,redisClient.db指向redisServer.db数组的其中一个元素,被指向的元素即为目标数据库

1
2
3
4
5
6
typedef struct redisClient {
// ...
// 记录客户端当前正在使用的数据库.
redisDb *db;
// ...
} redisClient;

下图为客户端的目标数据库为1号数据库。

客户端的目标数据库为1号数据库

问: SELECT命令的实现原理是什么?

通过修改redisClient.db指针,让它指向服务器中不同数据库,从而实现切换目标数据库的功能。

3 数据库键空间

Redis是一个键值对数据库服务器,服务器中的每个数据库都由一个redis.h/redisDb结构表示,其中,redisDb结构的dict字典保存了数据库中的所有键值对,我们将这个字典称为键空间(key space)

redis.h/redisDb结构:

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

其中,

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

示例:下图为包含了列表键、哈希表键、字符串键的结构图

包含了列表键、哈希表键、字符串键的结构图

注:数据库的键空间是一个字典,所以所有针对数据库的操作,实际上都是通过对键空间字典进行操作来实现的。

  • 添加新键: 添加新键值对到数据库,实际上就是将一个新键值对添加到键空间字典里面,其中键为字符串对象,而值则为任意一种类型的Redis对象。
  • 删除键: 删除数据库中的一个键,实际上就是在键空间里面删除键所对应额键值对对象。(键对象和值对象都要删除!
  • 更新键: 更新数据库键,实际上是对键空间里面键所对应的值对象进行更新,根据值对象的类型不同,更新的具体方法也会有所不同。
  • 对键取值: 取值,实际上就是在键空间中取出键所对应的值对象,根据值对象的类型不同,具体的取值方法也会有所不同。

3.1 其他键空间的操作

除了以上增删改查的操作,还有其他一些命令是通过对键空间进行处理来完成的。

比如说:

  • flushdb命令: 通过删除键空间中的所有键值对来实现的。
  • randomkey命令: 随机返回某个键,也是在键空间中随机返回的。
  • dbsize命令: 返回数据库键数量,通过返回键空间中包含的键值对的数量来完成。
  • exist、rename、keys等。

3.2 读写键空间时的维护操作

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

  • ①、在读取一个键之后(读写操作都会对键进行读取),服务器会根据键是否存在来更新服务器的键空间命中(keyspace_hit)/未命中(keyspace_misses)的次数。通过以下命令可查看其属性。

    1
    INFO stats
  • ②、在读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,这个值可以用于计算键的闲置时间,使用以下命令可查看键的闲置时间。

    1
    object idletime <key>
  • ③、若服务器在读取一个键时发现该键已过期,那么服务器会先删除这个过期键,然后执行余下的其他操作。

  • ④、若有客户端使用watch命令监视了这个键,那么服务器在对被监视的键进行修改之后,会标记这个键为脏(dirty),从而让事务成武注意到这个键已经被修改过。

  • ⑤、服务器每次修改一个键之后,都会对dity键计数器的值增1,这个计数器会触发服务器的持久化以及复制操作。

  • ⑥、若服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据通知。

4 数据库通知

数据库通知可以让客户端通过订阅给定频道或者模式,来获悉数据库中键的变化,以及数据中命令的执行情况。