Redis设计与实现之数据库

数据库的存储

数据库保存在 server.h/redisServer结构的redisDb *db中 , 其结构如下:

服务器中的数据库

redisServer中,存储了一个数据库列表, 在服务启动时, 会根据 dbnum 大小决定创建多少个数据库, dbnum大小有服务器配置的 database选项 决定, 默认值为 16

数据库的定义

代码定义在: server.h/redisDb

1
2
3
4
5
6
7
8
9
10
11
// redis数据库的定义
typedef struct redisDb {
dict *dict; /* 数据库的 keyspace */
dict *expires; /* 带有过期时间的 keys */
dict *blocking_keys; /* 阻塞等待数据的keys (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* 监控的 keys MULTI/EXEC CAS */
int id; /* 数据库 ID */
long long avg_ttl; /* Average TTL, just for stats */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

切换数据库

每个 redis 客户端都有自己的目标数据库, 每个客户端读取数据库写入或者读取命令时,目标数据库回程问这些命令的操作对象.

默认情况下, redis客户端目标数据库为 0 号数据库, 但是客户端可以通过 SELECT 命令来切换数据库

下图演示了数据库切换的操作:

数据库切换操作示例

在服务器内部, 客户端 client(server.h/client) 结构的 db 属性,记录了客户端当前的目标数据库, 这个属性是一个指向 redisDb结构的指针

1
2
3
4
typedef struct client {
// 其他属性省略
redisDb *db; /* 指向当前选中数据库的指针. */
} client;

client.db 只想的就是 server.db 中的一个元素, 而被指向的元素, 就是客户端的目标数据库.

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

拓展思考

假设我们当前有16个数据库, 当前已经向 id = 15 的数据库中写入了数据, 此时出于种种原因, 调整 databases = 8 , 重启服务会发生什么呢?

此问题分三个场景

作为纯内存缓存数据库使用

因为时纯内存的数据库, 重启时, 会清空全部数据, 再次启动时,不会有任何影响, 但是如果客户端未升级, 选择的数据库 >= 8, 此时客户端会报错

开启了持久化

若开启了持久化, 由于服务启动时会从持久化文件恢复数据, 则会产生数据无法恢复的情况, 进而导致服务无法启动 , 具体报错如下:

开启持久化,缩减database数量示例

缩减数量的同时,关闭持久化

在缩减database数量同时,关闭数据持久化,作为内存缓存使用,又会怎么样?

如果 未删除之前持久化的文件dump.rdb, 则服务依旧无法启动, 报错信息同 开启了持久化 的场景

如果删除了持久化的文件, 则服务可以正常启动, 场景同 作为纯内存缓存数据库使用

参考资料


Redis设计与实现之数据库
http://www.zhangdeman.cn/archives/ea5d39a5.html
作者
白茶清欢
发布于
2021年12月5日
许可协议