Redis 数据库
数据库
本章将对 Redis 数据库的构造和实现进行讨论。
除了说明数据库是如何储存数据对象之外,本章还会讨论键的过期信息是如何保存,而 Redis 又是如何删除过期键的。
数据库的结构
Redis 中的每个数据库,都由一个 redis.h/redisDb 结构表示:
typedef struct redisDb {
// 保存着数据库以整数表示的号码
int id;
// 保存着数据库中的所有键值对数据
// 这个属性也被称为键空间(key space)
dict *dict;
// 保存着键的过期信息
dict *expires;
// 实现列表阻塞原语,如 BLPOP
// 在列表类型一章有详细的讨论
dict *blocking_keys;
dict *ready_keys;
// 用于实现 WATCH 命令
// 在事务章节有详细的讨论
dict *watched_keys;
} redisDb;
下文将详细讨论 id 、 dict 和 expires 三个属性,以及针对这三个属性所执行的数据库操作。
数据库的切换
redisDb 结构的 id 域保存着数据库的号码。
这个号码很容易让人将它和切换数据库的 SELECT 命令联系在一起,但是,实际上,id 属性并不是用来实现 SELECT 命令,而是给 Redis 内部程序使用的。
当 Redis 服务器初始化时,它会创建出 redis.h/REDIS_DEFAULT_DBNUM 个数据库,并将所有数据库保存到 redis.h/redisServer.db 数组中,每个数据库的 id 为从 0 到 REDIS_DEFAULT_DBNUM - 1 的值。
当执行 SELECT number 命令时,程序直接使用 redisServer.db[number] 来切换数据库。
但是,一些内部程序,比如 AOF 程序、复制程序和 RDB 程序,需要知道当前数据库的号码,如果没有 id 域的话,程序就只能在当前使用的数据库的指针,和 redisServer.db 数组中所有数据库的指针进行对比,以此来弄清楚自己正在使用的是那个数据库。
以下伪代码描述了这个对比过程:
def PSEUDO_GET_CURRENT_DB_NUMBER(current_db_pointer):
i = 0
for db_pointer in redisServer.db:
if db_pointer == current_db_pointer:
break
i += 1
return i
有了 id 域的话,程序就可以通过读取 id 域来了解自己正在使用的是哪个数据库,这样就不用对比指针那么麻烦了。
数据库键空间
因为 Redis 是一个键值对数据库(key-value pairs database),所以它的数据库本身也是一个字典(俗称 key space):
在 redisDb 结构的 dict 属性中,保存着数据库的所有键值对数据。
下图展示了一个包含 number 、 book 、 message 三个键的数据库 ——其中 number 键是一个列表,列表中包含三个整数值;book 键是一个哈希表,表中包含三个键值对;而 message 键则指向另一个字符串: