在server.h中定义了redisServer结构,下面截取其中和数据库有关的两个部分
struct redisServer {
redisDb *db; /* 数据库数组,所有的数据库都存放在这里 */
int dbnum; /* 数据库总数 */
//...
};
可以看到,数据库是存放在redisDb结构的数组里的,下面是redisDb的结构
/* 数据库编号从0开始,默认情况下会使用0号数据库 */
typedef struct redisDb {
dict *dict; /* 键空间 */
dict *expires; /* 键的过期时间 */
dict *blocking_keys; /* 客户端等待数据的键(BLPOP)*/
dict *ready_keys; /* 阻塞键 */
dict *watched_keys; /* 监听键 */
int id; /* 数据库编号,从0开始 */
long long avg_ttl; /* 平均ttl */
} redisDb;
其中id表示当前数据库的编号,一般默认使用第0号数据库,可以通过命令切换数据库,例如切换到1号数据库:
来看看切换数据库的实现,非常简单,就是在redisDb数组中获取下标为id的那个DB:
int selectDb(client *c, int id) {
/* 校验id,server.dbnum就是数据库的总数 */
if (id < 0 || id >= server.dbnum)
return C_ERR;
c->db = &server.db[id];
return C_OK;
}
在redisDb结构中还有一个字典数组来存放所有的键值对,这里引用《redis设计与实现》一书中的示例来展示键值对在DB中的存储:
图中的StringObject、HashObject等对应着redis的对象,它们的实现可以查看前几篇介绍redis数据结构的文章。
对数据库的键值对做修改,其实就是对字典数组做修改。
例如,我们输入 set msg "hello world" 命令,这时会存在两种情况
- msg 这个键不存在,那么新增msg这个键,并且赋值为“hello world”
- msg 已经存在,那么复写msg所对应的value为“hello world”,其实就是“修改”
以下就是db中对这两种情况的实现:
void setKey(redisDb *db, robj *key, robj *val) {
/* 去dict中根据key获取value,有则返回value,没有则返回NULL */
if (lookupKeyWrite(db,key) == NULL) {/* 如果key不存在 */
/* 新建 */
dbAdd(db,key,val);
} else {/* 如果key存在 */
/* 修改 */
dbOverwrite(db,key,val);
}
/* val的引用计数+1 */
incrRefCount(val);
/* 如果key