redis 模块编程中 key value的生命周期

RedisModule_OpenKey

本节探讨下 RedisModule_OpenKey 中 key-value 何时释放。

首先 的数据结构如下

struct RedisModuleKey {
    ....
    robj *key;
    robj *value;
    ....
}

我们关心的是 RedisModuleKey 本身以及 其中 的 key和value值的生命周期

RedisModuleKey 本身 通过 RedisModule_OpenKey申请,模块内存管理自动将 RedisModuleKey 保存在ctx中。在指令执行完成之后,调用moduleFreeContext->autoMemoryCollect->RM_CloseKey->moduleCloseKey尝试进行释放。

可以看到 一次完整的命令,RedisModuleKey 本身会被无情释放。

void RM_CloseKey(RedisModuleKey *key) {
    if (key == NULL) return;

    moduleCloseKey(key);
    autoMemoryFreed(key->ctx,REDISMODULE_AM_KEY,key);
    zfree(key);//彻底释放
}

所以,RedisModule_OpenKey 的本质可以理解为只是一个临时变量

现在我们来看RedisModuleKey其中的 key值,通过下面可以看到,key的引用计数减1。

/* Destroy a RedisModuleKey struct (freeing is the responsibility of the caller). */
static void moduleCloseKey(RedisModuleKey *key) {
    int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);
    if ((key->mode & REDISMODULE_WRITE) && signal)
        signalModifiedKey(key->ctx->client,key->db,key->key);
    /* TODO: if (key->iter) RM_KeyIteratorStop(kp); */
    RM_ZsetRangeStop(key);

    decrRefCount(key->key);
}

所以key会被无情释放么?不一定,如果 RedisModuleKey 入参的 key 引用计数为1且模式是write模式,那么在 RM_OpenKey->moduleInitKey中,引用计数会被++。也就是RedisModule_OpenKey返回后,key的引用计数在原先的基础上加1,那么在 moduleCloseKey中,key就不会被释放。

如果 如果大家这么用:RedisModule_OpenKey(ctx, argv[1]...,即喜欢把用户的入参作为open的参数,虽然 RedisModule_OpenKey返回后 argv[1]的引用计数是2,但是arg参数本身会在redis框架最外层释放一次,所以argv[1] 不会被保留在内存里面。
但是 如果你这么用:RedisModule_OpenKey(ctx, RedisModule_CreateString(NULL, s, (size_t)slen)...),那么 没人会释放 RedisModule_CreateString创建的内存,此时要么 RedisModule_CreateString第一个入参传入ctx然后依靠redis自动内存管理来自动释放内存,要么自己主动调用free来减引用计数。

再来看看value值,module通过RedisModule_ModuleTypeSetValue来设置 value值,其本质就是genericSetKey,将 RedisModuleKey中的key-value 保存到redis的db中,

int RM_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value) {
    if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;

    /*旧的value如果存在,例如你之前执行过此函数,旧的value引用计数会被减1*/
    RM_DeleteKey(key);
    robj *o = createModuleObject(mt,value);

    genericSetKey(key->ctx->client,key->db,key->key,o,0,0);
    /*到这 o的应用计数为2*/
    decrRefCount(o);
    /*到这 o的应用计数为1*/
    key->value = o;
    return REDISMODULE_OK;
}

所以,value值的引用计数,在 通过 RedisModule_ModuleTypeSetValue后并没有增减。也就是value值的生命周期没有改变。

RedisModule_DeleteKey其实就是 dbDelete的封装,RedisModule_ModuleTypeSetValue将key-value保存在db中,然后并没有增加减少他们的引用计数,RedisModule_DeleteKey因为调用了dbDelete所以会直接将key以及value的引用计数减1。

总结下:
1、RedisModule_OpenKey 返回的RedisModuleKey指针,无论如何都会被释放。其中,RedisModule_OpenKey 会对入参的key,引用计数加一。然后在释放 RedisModuleKey时,减1,如果其他地方需要用这个 如果 入参key原先引用计数为1,那么需要额外的操作才能将其释放。
2、如果进行了 RedisModule_ModuleTypeSetValue,那么,RedisModuleKey总的key和value被存在db,其中,key 是拷贝一份的,我们所有不关心。value被db直接引用了(指针直接指向了value)。RedisModule_ModuleTypeSetValue 本身不会对key和value引用计数进行增删。
3、通过DEL命令,删除 RedisModule_OpenKey中指定的key时,和标准流程一样,对 value引用计数减1,RedisModule_DeleteKey 也是一样的逻辑。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页