Redis设计与实现之数据库
数据库的存储
数据库保存在 server.h/redisServer结构的redisDb *db中
, 其结构如下:
redisServer中,存储了一个数据库列表, 在服务启动时, 会根据 dbnum
大小决定创建多少个数据库, dbnum大小有服务器配置的 database选项
决定, 默认值为 16
数据库的定义
代码定义在: server.h/redisDb
// 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结构的指针
typedef struct client {
// 其他属性省略
redisDb *db; /* 指向当前选中数据库的指针. */
} client;
client.db
只想的就是 server.db
中的一个元素, 而被指向的元素, 就是客户端的目标数据库.
重要
通过修改 client.db 的指针, 让它指向服务器中不同的数据库, 从而实现切换目标数据库的功能 , 这就是 SELECT 指令的实现原理
拓展思考
假设我们当前有16个数据库, 当前已经向 id = 15 的数据库中写入了数据, 此时出于种种原因, 调整 databases = 8 , 重启服务会发生什么呢?
此问题分三个场景
作为纯内存缓存数据库使用
因为时纯内存的数据库, 重启时, 会清空全部数据, 再次启动时,不会有任何影响, 但是如果客户端未升级, 选择的数据库 >= 8, 此时客户端会报错
开启了持久化
若开启了持久化, 由于服务启动时会从持久化文件恢复数据, 则会产生数据无法恢复的情况, 进而导致服务无法启动
, 具体报错如下:
缩减数量的同时,关闭持久化
在缩减database数量同时,关闭数据持久化,作为内存缓存使用,又会怎么样?
如果 未删除之前持久化的文件dump.rdb, 则服务依旧无法启动, 报错信息同 开启了持久化
的场景
如果删除了持久化的文件, 则服务可以正常启动, 场景同 作为纯内存缓存数据库使用
参考资料
参考内容
书籍
: 《Redis 设计与实现》
实体书
: 可在京东搜索购买
注意事项
《Redis 设计与实现》 本书讲解基于redis 3.x 版本, 本人整理基于redis 5.0.6,期间跨了两个大版本, 部分功能具体实现已发生变化, 如发现整理内容与 《Redis 设计与实现》 讲解不一致, 可加 QQ : 2215508028 沟通,或在相关章节评论留言,会自动同步至issue
整理内容时参考的redis源码
版本
: 5.0.6
官方git
: redis官方GIT (commit :bb78454b0fef0dc5903328d037ac2520108e0044 (5.0.6版本,redis官方的commit))