Redis源码剖析和注释(十四)---- Redis 数据库及相关命令实现(db)

Redis 数据库及相关命令实现

1. 数据库管理命令

数据库管理的命令如下表格所示:redis keys命令详解

命令 描述
FLUSHDB 清空当前数据库的所有key
FLUSHALL 清空整个Redis服务器的所有key
DBSIZE 返回当前数据库的key的个数
DEL key [key …] 删除一个或多个键
EXISTS key 检查给定key是否存在
SELECT id 切换到指定的数据库
RANDOMKEY 从当前数据库中随机返回(不删除)一个 key 。
KEYS pattern 查找所有符合给定模式pattern的key
SCAN cursor [MATCH pattern] [COUNT count] 增量式迭代当前数据库键
LASTSAVE 返回最近一次成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示。
TYPE key 返回指定键的对象类型
SHUTDOWN 停止所有客户端,关闭 redis 服务器(server)
RENAME key newkey 重命名指定的key,newkey存在时覆盖
RENAMENX key newkey 重命名指定的key,当且仅当newkey不存在时操作
MOVE key db 移动key到指定数据库
EXPIREAT key timestamp 为 key 设置生存时间,EXPIREAT 命令接受的时间参数是 UNIX 时间戳
EXPIRE key seconds 以秒为单位设置 key 的生存时间
PEXPIRE key milliseconds 以毫秒为单位设置 key 的生存时间
PEXPIREAT key milliseconds-timestamp 以毫秒为单位设置 key 的过期 unix 时间戳
TTL key 以秒为单位返回 key 的剩余生存时间
PTTL key 以毫秒为单位返回 key 的剩余生存时间

2. 数据库的实现

2.1数据库的结构

typedef struct redisDb {
    // 键值对字典,保存数据库中所有的键值对
    dict *dict;                 /* The keyspace for this DB */
    // 过期字典,保存着设置过期的键和键的过期时间
    dict *expires;              /* Timeout of keys with a timeout set */
    // 保存着 所有造成客户端阻塞的键和被阻塞的客户端
    dict *blocking_keys;        /*Keys with clients waiting for data (BLPOP) */
    // 保存着 处于阻塞状态的键,value为NULL
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    // 事物模块,用于保存被WATCH命令所监控的键
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    // 当内存不足时,Redis会根据LRU算法回收一部分键所占的空间,而该eviction_pool是一个长为16数组,保存可能被回收的键
    // eviction_pool中所有键按照idle空转时间,从小到大排序,每次回收空转时间最长的键
    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */
    // 数据库ID
    int id;                     /* Database ID */
    // 键的平均过期时间
    long long avg_ttl;          /* Average TTL, just for stats */
} redisDb;
  • blocking_keys 和 ready_keys 使用于在列表类型的阻塞命令(BLPOP等),详细内容看:Redis 列表键命令实现
  • watched_keys 是用于事物模块。
  • eviction_pool 是Redis在内存不足情况下,要回收内存时所使用。
  • dict 和 expires 和 id是本篇主要讨论的。

Redis服务器和客户端也都保存有数据库的信息,下面截取出来:

typedef struct client {
    redisDb *db;            /* Pointer to currently SELECTed DB. */
} client;

struct redisServer {
    redisDb *db;
    int dbnum;                      /* Total number of configured DBs */
};

Redis服务器在初始化时,会创建一个长度为dbnum(默认为16)个 redisDb类型数组,客户端登录时,默认的数据库为0号数据库。当执行SELECT index命令后,就会切换数据库。我们用两个客户端,表示如下图:

这里写图片描述

SELECT index命令非常简单,源码如下:

// 切换数据库
int selectDb(client *c, int id) {
    // id非法,返回错误
    if (id < 0 || id >= server.dbnum)
        return C_ERR;
    // 设置当前client的数据库
    c->db = &server.db[id];
    return C_OK;
}

2.2 数据库的键值对字典

Redis是一个key-value数据库服务器,它将所有的键值对都保存在 redisDb 结构中的 dict 字典成员中(Redis 字典结构源码剖析)。

  • 键值对字典的键,就是数据库的key,每一个key都是字符串的对象。

  • 键值对字典的值,就是数据库的value,每一个value可以是字符串的对象,列表对象,哈希表对象,集合对象和有序集合对象中的任意一种。

Redis 对象系统源码剖析

数据库对键对象的删除操作,会连带值对象也一并删除,因此再有一些操作中,例如RENAME等命令,中间步骤会使用删除原有键,常常需要对值对象的引用计数加1,保护值对象不被删除,当新的键被设置后,则对值对象的引用计数减1。

我们向一个数据库中添加几个键,并且用图表示出来:

  • 红色代表键对象,有 RAW编码的字符串对象,哈希对象。将结构简化表示,重点关注引用计数。
  • 蓝色代表值对象,完成结构如图所示。

这里写图片描述

数据库每次根据键名找到值对象时,是分为以读操作 lookupKeyRead() 或写操作 lookupKeyWrite() 的方式取出的,而这两种有一定的区别,下面展示源码:

  • lookupKey()函数

读操作 lookupKeyRead() 或写操作 lookupKeyWrite()都会调用这个底层的函数,这个函数非常简单,就是从键值对字典中先找到键名对应的键对象,然后取出值对象。

// 该函数被lookupKeyRead()和lookupKeyWrite()和lookupKeyReadWithFlags()调用
// 从数据库db中取出key的值对象,如果存在返回该对象,否则返回NULL
// 返回key对象的值对象
robj *lookupKey(redisDb *db, robj *key, int flags) {
    // 在数据库中查找key对象,返回保存该key的节点地址
    dictEntry *de = dictFind(db->dict,key->ptr);
    if (de) {   //如果找到
        robj *val = dictGetVal(de); //取出键对应的值对象

        /* Update the access time for the ageing algorithm.
         * Don't do it if we have a saving child, as this will trigger
         * a copy on write madness. */
        // 更新键的使用时间
        if (server.rdb_child_pid == -1 &&
            server.aof_child_pid == -1 &&
            !(flags & LOOKUP_NOTOUCH))
        {
            val->lru = LRU_CLOCK();
        }
        return val; //返回值对象
    } else {
        return NULL;
    }
  • lookupKeyRead()函数

lookupKeyRead()函数调用了lookupKeyReadWithFlags()函数,后者其实就判断了一下当前键是否过期,如果没有过期,更新 misses 和 hits 信息,然后就返回值对象。

还有就是两个宏:

  1. define LOOKUP_NONE 0 //zero,没有特殊意义
  2. define LOOKUP_NOTOU
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值