结合redis设计与实现的redis源码学习-11-数据库(server.h/redisDb,notify.c)

本文介绍了Redis服务器中的数据库结构redisDb,包括键空间及其维护操作、过期键处理策略及数据库通知机制。深入探讨了键的增删改查操作及服务器在读写键空间时的维护工作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在之前的学习中我们了解了redis所有的数据结构和使用方法,现在我们开始来学习redis数据库相关的内容。
这一节先来学习服务器中的数据库,主要代码在server.h和Db.c中。
redis服务器的所有数据库都保存在服务器状态redisServer结构体的db数组中,db数据的每一个项都是redisDb结构,每个redisDb结构代表一个数据库,其中dict字典保存了数据库中的所有键值对,这个dict就是数据库的建空间;
键空间和用户所见的数据库是直接对应的,键空间的键也就是数据库的键,每个都是一个字符串对象,键空间的值也就是数据库的值,可以是任何一种redis对象;

redisDb结构体:

/* Redis database representation. There are multiple databases identified by integers from 0 (the default database) up to the max configured database. The database number is the 'id' field in the structure. 这个多数据库认证使用从0开始的整数指导最大值,这个数就是id*/
typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB 数据库的键空间*/
    dict *expires;              /* Timeout of keys with a timeout set 过期key的字典*/
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) 客户端等带数据的key*/
    dict *ready_keys;           /* Blocked keys that received a PUSH 被阻止的key,收到推送*/
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS 事物watch的key*/
    struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys 去除池的key*/
    int id;                     /* Database ID 数据库id*/
    long long avg_ttl;          /* Average TTL, just for stats 平均过期时间*/
} redisDb;
键空间的增删改查:

1、增:添加一个新键值对到数据库,实际上就是将一个新的键值对添加到键空间的字典中;
2、删:删除数据库中的一个键,就是在键空间删除键所对应的键值对对象;
3、改:就是对键空间中键所对应的值进行更新;
4、查:对一个数据库的键进行取值,就是在键空间中取出值对象,根据值类型的不同,方法也有所不同;
其他操作:
flushdb,randomkey,dbsize,exists,rename,keys等操作都是通过对键空间操作完成的。
读写键空间时的维护操作:

当redis对数据库进行读写时,服务器还会额外的进行一些维护操作:

1、读取一个键之后,服务器会根据键是否存在来更新键空间命中次数或者不命中次数;
2、在读取一个键之后,服务器会更新LRU时间;
3、在读取时发现键已过期,服务器会先删除这个键,然后执行其他操作;
4、如果客户端watch了某个键,那么服务器在对被监视的键进行修改后,会将这个键标记为dirty,从而让事务程序发现这个键已经被修改;
5、服务器每次修改一个键之后,都会对脏键计数器增1,会触发持久化及复制操作;
6、如果服务器开启了数据库通知功能,那么在对键进行修改后,会按配置发送相应的数据库通知。

redis键的过期时间:

过期键会保存在expire的字典中,这个字典的键是指针,指向键空间中的某个键对象,值是long long的指数,一个时间戳。

过期键的删除策略:

1、惰性删除:每次访问时判断该键是否应该删除;
2、定期删除:函数activeexpireCycle函数每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,删除过期键;

数据库通知:

发送数据库通知的功能是由notify.c/notifyKeyspaceEvent函数实现的;当一个redis命令需要发送通知的时候,就该调用这个函数,并向其传递事件的相关信息;
notify.c比较短,就在这里看吧:

#include "server.h"
/* Turn a string representing notification classes into an integer representing notification classes flags xored. The function returns -1 if the input contains characters not mapping to any class. 将表示通知的字符串转化为整数,如果不映射到任何字符,则返回-1*/
int keyspaceEventsStringToFlags(char *classes) {
    char *p = classes;
    int c, flags = 0;

    while((c = *p++) != '\0') {
        switch(c) {
        case 'A': flags |= NOTIFY_ALL; break;
        case 'g': flags |= NOTIFY_GENERIC; break;
        case '$': flags |= NOTIFY_STRING; break;
        case 'l': flags |= NOTIFY_LIST; break;
        case 's': flags |= NOTIFY_SET; break;
        case 'h': flags |= NOTIFY_HASH; break;
        case 'z': flags |= NOTIFY_ZSET; break;
        case 'x': flags |= NOTIFY_EXPIRED; break;
        case 'e': flags |= NOTIFY_EVICTED; break;
        case 'K': flags |= NOTIFY_KEYSPACE; break;
        case 'E': flags |= NOTIFY_KEYEVENT; break;
        default: return -1;
        }
    }
    return flags;
}

/* This function does exactly the revese of the function above: it gets as input an integer with the xored flags and returns a string representing the selected classes. The string returned is an sds string that needs to be released with sdsfree(). 这个函数完成上面的函数:输入一个整数,返回一个表示所选类的字符串。这个返回的字符串需要使用sdsfree释放*/
sds keyspaceEventsFlagsToString(int flags) {
    sds res;

    res = sdsempty();
    if ((flags & NOTIFY_ALL) == NOTIFY_ALL) {
        res = sdscatlen(res,"A",1);
    } else {
        if (flags & NOTIFY_GENERIC) res = sdscatlen(res,"g",1);
        if (flags & NOTIFY_STRING) res = sdscatlen(res,"$",1);
        if (flags & NOTIFY_LIST) res = sdscatlen(res,"l",1);
        if (flags & NOTIFY_SET) res = sdscatlen(res,"s",1);
        if (flags & NOTIFY_HASH) res = sdscatlen(res,"h",1);
        if (flags & NOTIFY_ZSET) res = sdscatlen(res,"z",1);
        if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
        if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
    }
    if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
    if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
    return res;
}

/* The API provided to the rest of the Redis core is a simple function: notifyKeyspaceEvent(char *event, robj *key, int dbid);'event' is a C string representing the event name. 'key' is a Redis object representing the key name. 'dbid' is the database ID where the key lives.  event是字符串的时间名称,key是redis的最想指针,也就是产生时间的键,dbid是产生事件的数据库号码*/
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) {
    sds chan;
    robj *chanobj, *eventobj;
    int len = -1;
    char buf[24];

    /* If notifications for this class of events are off, return ASAP. 如果给定的通知不是服务器允许发送的通知,则返回*/
    if (!(server.notify_keyspace_events & type)) return;

    eventobj = createStringObject(event,strlen(event));

    /* __keyspace@<db>__:<key> <event> notifications. 发送键空间通知*/
    if (server.notify_keyspace_events & NOTIFY_KEYSPACE) {
        chan = sdsnewlen("__keyspace@",11);
        len = ll2string(buf,sizeof(buf),dbid);
        chan = sdscatlen(chan, buf, len);
        chan = sdscatlen(chan, "__:", 3);
        chan = sdscatsds(chan, key->ptr);
        chanobj = createObject(OBJ_STRING, chan);
        pubsubPublishMessage(chanobj, eventobj);//发送通知
        decrRefCount(chanobj);//对象引用计数+1
    }

    /* __keyevente@<db>__:<event> <key> notifications. 发送键事件通知*/
    if (server.notify_keyspace_events & NOTIFY_KEYEVENT) {
        chan = sdsnewlen("__keyevent@",11);
        if (len == -1) len = ll2string(buf,sizeof(buf),dbid);
        chan = sdscatlen(chan, buf, len);
        chan = sdscatlen(chan, "__:", 3);
        chan = sdscatsds(chan, eventobj->ptr);
        chanobj = createObject(OBJ_STRING, chan);
        pubsubPublishMessage(chanobj, key);
        decrRefCount(chanobj);//对象引用计数+1
    }
    decrRefCount(eventobj);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值