经过上篇文章,介绍了 Redis 中的对象,Redis 是通过这些对象来保存 Redis 数据库中的数据的。这篇我们就看一下Redis数据库在内存中的结构,以及客户端切换数据库、数据库保存键值对、以及对数据的增删改查等操作的实现方法。
我们本地安装 Redis 会安装redis-cli 和 redis-server两个部分,相当于一个Redis的客户端和服务端。我们通常通过redis-cli来连接redis-server,数据都是存放在redis-server的数据库中,我们来看一下服务器中redis-server和Redis数据库结构。
redis-server
Redis 服务器将所有数据库都保存在结构 redis.h/redisServer 中的 db 数组中,db 中的每一项都是 redis.h/redisDb 结构,代表一个数据库。
struct redisServer { // ... // 一个数组,保存着服务器中所有数据库 redisDb *db; // 服务器中数据库数量 int dbnum; };
dbnum 属性由配置项 database 决定。默认情况下为16。
切换数据库
每个redis-cli都有自己目标数据库,当客户端执行数据库读或者写命令时,目标数据库将成为命令的操作对象。
默认情况下,redis-cli都会连接第0个数据库,客户端可以通过 SELECT 命令切换数据库。如下例子:
尝试用redis-cli 连接本地redis-server:
访问被拒绝了,是因为我们没在本地启动redis-server。我们启动并连接本地redis-server:
(上述例子是在macOS系统中,Windows和Linux系统都是大同小异,网上都可以找到解决方案。)
我们连接本地redis-server,并做一些操作:
可以看到,我们现在数据库0设置language,再切换为数据库15设置language,保存了两个key,说明redis-server中每个数据库是独立的。
但是切换数据库是怎么做到的呢?我们来看一下redis-cli的结构
typedef struct redisClient { // ... // 记录当前客户端正在使用的数据库 // redisDb *db // ... } redisClient;
redisClient.db 指针指向redisServer.db数组中的其中一个元素,代表正在连接那个被客户端指向的数据库,如下图例子:
通过修改redisClient.db指针,指向redisServer中不同的数据库,实现切换数据库的功能,这就是 SELECT 命令的实现原理。
数据库键空间
Redis 是个键值对数据库,服务器中的每个数据库由 redis.h/redisDb 结构表示,其中redisDb中的dict字典保存了数据库中的所有键值对。
typedef struct redisDb { // ...
// 数据库键空间,保存着数据库中所有键值对 // dict *dict // ... } redisDb;
键空间的键就是数据库的键,每一个键都是一个字符串对象。
键空间的值近视数据库的值,每一个值可以是字符串对象、列表对象、哈希对象、集合对象和有续集合对象中的任意一个。
举个例子,我们对数据库执行如下命令:
那么数据库键值对在内存中的结构如下图所示:
因为数据库键空间就是一个字典,所有对Redis数据库的增删改查,相当于对键空间(字典)的增删改查。
参考文献
Redis设计与实现第二版