redis db.c数据库底层操作的源码分析

redis的所有数据库都是保存在redisServer结构体的db数组中。db.c主要是封装了数据库的底层操作实现。
摘要由CSDN通过智能技术生成

       redis的所有数据库都是保存在redisServer结构体的db数组中,db数组中的每个元素都是redisDb结构体代表一个数据库。db.c主要是封装了数据库的底层操作实现,操作dict和expires两个字典。所有数据库的键值队都是保存在dict字典(即内存中),而expires用来保存设置过期时间的键。redis用rdb和aof来进行数据的持久化,仅仅是内存数据备份,当内存不足时,通过内存策略删除部分键值对,不会持久化要删除的的键值队。

      数据库的基本结构:

struct redisServer {
    ……
    redisDb *db;//redis server的所有数据裤
    int dbnum;//redis server的数据库的个数
    ……
};
typedef struct redisDb {
    dict *dict;                 /*数据库的所有的键值队*/
    dict *expires;              /* 设置的过期的key,key-timeout */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* 事务中被监视的key*/
    struct evictionPoolEntry *eviction_pool;    /* pool of keys */
    int id;                //数据库的id
    long long avg_ttl;          /* Average TTL, just for stats */
} redisDb;

     db->dict的操作

/*<--------------获取key->val------------------->*/
//从db->dict字典中获取key的val,并更新val的lru,用于lru的内存策略
robj *lookupKey(redisDb *db, robj *key);
//封装了lookupKey,增加了过期判断和miss记录,用于读命令的调用
robj *lookupKeyRead(redisDb *db, robj *key) {
    robj *val;
    //如果key已经过期,从db->dict中删除
    if (expireIfNeeded(db,key) == 1) {
        //server是master
        if (server.masterhost == NULL) return NULL;
        //server是slave,client是master,命令为只读
        if (server.current_client &&server.current_client != server.master &&
            server.current_client->cmd &&server.current_client->cmd->flags & CMD_READONLY)
        {
            return NULL;
        }
    }
    val = lookupKey(db,key);
    //更新server的key的miss和hit
    if (val == NULL)
        server.stat_keyspace_misses++;
    else
        server.stat_keyspace_hits++;
    return val;//返回key的value
}//封装了lookupKeyRead,当查找不到key是返回客户端指定reply,用于读命令的调用
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply);
//封装了lookupKey,增加了过期判断,用于写命令的调用
robj *lookupKeyWrite(redisDb *db, robj *key) {
    expireIfNeeded(db,key);
    return lookupKey(db,key);
}//封装了lookupKeyWrite,当查找不到key是返回客户端指定reply,用于写命令的调用
robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
/*<--------------获取key->val------------------->*/
//添加键值队到数据库
void dbAdd(redisDb *db, robj *key, robj *val) {
    sds copy = sdsdup(key->ptr);
    int retval = dictAdd(db->dict, copy, val);
    //val是list对象,将key从bloking_keysh转移到ready_keys字典中
    if (val->type == OBJ_LIST) signalListAsReady(db, key);
    //集群模式下,将key-slot信息记录到slots_to_keys
    if (server.cluster_enabled) slotToKeyAdd(key);
 }//修改数据库中key的val
 void dbOverwrite(redisDb *db, robj *key, robj *val) {
    dictEntry *de = dictFind(db->dict,key->ptr);
    serverAssertWithInfo(NULL,key,de != NULL);
    dictReplace(db->dict, key->ptr, val);
}//删除数据库中key-val
int dbDelete(redisDb *db, robj *key) {
    //删除expires中的key信息
    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
    if (dictDelete(db->dict,key->ptr) == DICT_OK) {
        //集群模式下删除,key-slot的信息
        if (server.cluster_enabled) slotToKeyDel(key);
        return 1;
    } else {
        return 0;
    }
}//设置val为字符串对象的键值队
 void setKey(redisDb *db, robj *key, robj *val) {
    if (lookupKeyWrite(db,key) == NULL) {
        dbAdd(db,key,val);
    } else {
        dbOverwrite(db,key,val);
    }//增加字符串对象的引用计数
    incrRefCount(val);
    removeExpire(db,key);//从expires中移除key-timeout
    signalModifiedKey(db,key);//触发被监视的键被修改的信息
}

int dbExists(redisDb *db, robj *key);//判断key是否存在数据库
robj *dbRandomKey(redisDb *db);//从数据库随机返回一个key

     db->expires的操作

int removeExpire(redisDb *db, robj *key);//从expires中删除key的信息
void setExpire(redisDb *db, robj *key, long long when);//设置key的过期时间
long long getExpire(redisDb *db, robj *key);//获取key的过期时间
//判断设置过期时间的key是否过期,删除过期的key,
int expireIfNeeded(redisDb *db, robj *key) {
    mstime_t when = getExpire(db,key);
    mstime_t now;
    if (when < 0) return 0;
    if (server.loading) return 0;
    now = server.lua_caller ? server.lua_time_start : mstime();
    if (server.masterhost != NULL) return now > when;
   //判断key是否过期
    if (now <= when) return 0;
    //记录server删除的过期健的个数
    server.stat_expiredkeys++;
    // 过期健的传播
    propagateExpire(db,key,server.lazyfree_lazy_expire);
    notifyKeyspaceEvent(NOTIFY_EXPIRED,"expired",key,db->id);
    //删除过期的键
    return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
                                         dbSyncDelete(db,key);
}//过期健传播
void propagateExpire(redisDb *db, robj *key, int lazy) {
    robj *argv[2];
    argv[0] = lazy ? shared.unlink : shared.del;
    argv[1] = key;
    incrRefCount(argv[0]);
    incrRefCount(argv[1]);
    if (server.aof_state != AOF_OFF)
        //将过期健以删除命令写入aof文件
        feedAppendOnlyFile(server.delCommand,db->id,argv,2);
    //传播过期健给slave
    replicationFeedSlaves(server.slaves,db->id,argv,2);
    decrRefCount(argv[0]);
    decrRefCount(argv[1]);
}

       在集群模式下,slots_to_keys用于记录每个槽中key的存放信息。这些信息用于槽迁移时候,能准确
迁移槽中的键值队。

void slotToKeyAdd(robj *key) ;//将key添加跳跃表slots_to_keys中对应的slot中
void slotToKeyDel(robj *key) ;//从key所在slot删除指定key
void slotToKeyFlush(void);//重置跳跃表的slots_to_keys中的所有信息
unsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count);
//从指定的slot随机获取count个key
unsigned int delKeysInSlot(unsigned int hashslot);//删除指定slot中所有的key信息
unsigned int countKeysInSlot(unsigned int hashslot);//获取指定槽中key的数量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值