redis module 学习—官网文档整理
前言
redis在4.0版本中,推出了一个非常吸引的特性,可以通过编写插件的模式,来动态扩展redis的能力。在4.0之前,如果用户想拥有一个带TTL的INCRBY 命令,那么用户只能自己去改代码,重新编译了。在4.0版本推出之后,想实现一个自定义的命令就简单的多了。
在这个功能发布之后,已经有许多的第三方扩展插件被开发出来。具体可以参见官方模块仓库 :
接下来,本文将基于redis官方文档 进行翻译,介绍下相关的功能(翻译的和原有有出入,更多是基于自己的理解。如果大家有问题,欢迎讨论),也算对这个功能的学习笔记。
Loading modules (如何加载模块)
加载模块一共有2中方式:
- 在redis.conf 配置文件中指定:
loadmodule /path/to/mymodule.so
- 运行时使用命令加载模块:
MODULE LOAD /path/to/mymodule.so
也可以通过命令查看当前有多少模块被加载了:
MODULE LIST
如果加载了模块之后想移除,也可以通过如下的命令移除模块:
MODULE UNLOAD mymodule
The simplest module you can write (写一个简单的模块)
redis官网上提供了一个非常简单的示例,演示了通过模块添加一个返回随机数的命令:
#include "redismodule.h"
#include <stdlib.h>
int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_ReplyWithLongLong(ctx,rand());
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (RedisModule_Init(ctx,"helloworld",1,REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"helloworld.rand",
HelloworldRand_RedisCommand) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
在示例中有2个函数,HelloworldRand_RedisCommand 这个函数用于实现新添加的命令。而RedisModule_OnLoad函数是每个模块中都必须存在的。这个函数是一个redis在加载模块的时候的入口(初始化模块,注册命令,初始化数据结构等工作),原文文档是这么描述这个入口函数的:
It is the entry point for the module to be initialized, register its commands, and potentially other private data structures it uses.
那么要如何调用这个新的命令呢?原文文档中,给出的提示是,最好通过{模块名}.{命令名称}这样子的格式来调用。同时也有一个要注意的地方:
两个不同的模块下,如果有相同的名称的命令,则 RedisModule_CreateCommand 这个创建命令的函数会失败。
Note that if different modules have colliding commands, they'll not be able to work in Redis at the same time, since the function RedisModule_CreateCommand will fail in one of the modules, so the module loading will abort returning an error condition.
Module initialization (模块初始化)
前文介绍如何写一个简单的模块中,RedisModule_OnLoad函数里面调用如下的2个函数 :
- RedisModule_Init
- RedisModule_CreateCommand
接下来依据redis文档分别介绍下两个函数。
RedisModule_Init
官方文档给出的函数定义的形式如下:
int RedisModule_Init(RedisModuleCtx *ctx, const char *modulename,
int module_version, int api_version);
这个函数必须在redis模块初始化的时候被第一个调用,调用的时候会注册如下的信息:
- 模块名称:即参数const char *modulename
- 模块的版本:即参数int module_version
- API的版本:即参数 int api_version
如果有以下的错误发生,则这个函数返回错误REDISMODULE_ERR :
- API的版本错误
- 模块名称已经被其他模块注册过
- 其他相似的错误
RedisModule_CreateCommand
官方文档给出的函数定义的形式如下:
int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *cmdname,
RedisModuleCmdFunc cmdfunc);
RedisModule_CreateCommand函数用于注册一个新的命令。 可以看出,RedisModule_CreateCommand需要传入如下的三个参数:
参数 | 说明 |
---|---|
RedisModuleCtx *ctx | 第一个参数是 redis module 的上下文指针(姑且这么称呼吧,原文中是这么描述这个参数的:As you can see, most Redis modules API calls all take as first argument the context of the module, so that they have a reference to the module calling it, to the command and client executing a given command, and so forth.) |
const char *cmdname | 命令的名称,例如上文就是 "helloworld.rand" |
RedisModuleCmdFunc cmdfunc | 命令实现的函数指针,例如上文的HelloworldRand_RedisCommand |
RedisModuleCmdFunc
上文中可以看到,RedisModule_CreateCommand创建命令的函数最后一个参数是RedisModuleCmdFunc。这个参数指向实现当前注册的命令的函数。 例如在前文的例子就是:
int HelloworldRand_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_ReplyWithLongLong(ctx,rand());
return REDISMODULE_OK;
}
一个命令的实现函数必须有如下的形式:
int mycommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
参数 | 说明 |
---|---|
RedisModuleCtx *ctx | module的指针,前文介绍过了。 |
RedisModuleString **argv | 新命令需要的参数 |
int argc | 参数的个数 |
Setup and dependencies of a Redis module (这节具体说了怎么编译和生成redis 模块)
文档中这节说的很简略:
Redis modules don't depend on Redis or some other library, nor they need to be compiled with a specific redismodule.h file. In order to create a new module, just copy a recent version of redismodule.h in your source tree, link all the libraries you want, and create a dynamic library having the RedisModule_OnLoad() function symbol exported.
The module will be able to load into different versions of Redis.
简单的来说就是:
- Redis模块不依赖于Redis或其他库,也不需要使用特定的redismodule.h文件进行编译。
- 只需在源代码树中复制最新版本的redismodule.h,链接所需的所有库,并创建一个导出RedisModule_OnLoad()函数符号的动态库。
Passing configuration parameters to Redis modules (传递参数给redis 模块)
加载模块的时候,也是可以传递参数给需要加载的模块的。例如下面的例子:
loadmodule mymodule.so foo bar 1234
传递给module参数 foo bar 1234 这几个参数。这些参数会在RedisModule_OnLoad 函数里面的RedisModuleString **argv这个参数里获取到。
Working with RedisModuleString objects(RedisModuleString的使用)
在module中,经常使用到RedisModuleString这种类型的变量。比如前文中提到的RedisModule_OnLoad函数的入参RedisModuleString **argv,以及redis module API的返回值等等都用到了这个类型。
在官方文档中提供了多个函数,方便对于RedisModuleString的操作:
- const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);
- RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);
- void RedisModule_FreeString(RedisModuleString *str);
- RedisModule_CreateStringFromLongLong(ctx,10);
RedisModule_StringPtrLen
有些时候需要从RedisModuleString 里面获取原始的C字符串,以便传递给其他的类型的接口。 Redis提供了 RedisModule_StringPtrLen函数,用于实现这个功能。
const char *RedisModule_StringPtrLen(RedisModuleString *string, size_t *len);
从函数定义可以看出,RedisModule_StringPtrLen返回一个char 指针,并把这个字符串的长度设置到len当中。
RedisModule_CreateString 和 RedisModule_FreeString
前文提到了怎么从 RedisModuleString里面获取C原始的字符串。 那么如何从一个C字符串创建一个RedisModuleString 呢? 答案是可以通过 RedisModule_CreateString函数创建一个。函数的定义如下:
RedisModuleString *RedisModule_CreateString(RedisModuleCtx *ctx, const char *ptr, size_t len);
使用 RedisModule_CreateString 创建的字符串,必须使用 RedisModule_FreeString 去释放。
void RedisModule_FreeString(RedisModuleString *str);
当然如果想偷懒,不每次都去释放创建的对象的话,也可以使用redis的在 automatic memory management,这个特性在后面的文档会介绍到。
RedisModule_CreateStringFromLongLong
有些时候,需要从整数中创建一个字符串,可以使用函数 RedisModule_CreateStringFromLongLong 。
RedisModuleString *mystr = RedisModule_CreateStringFromLongLong(ctx,10);
官方文档给出的例子如下:
long long myval;
if (RedisModule_StringToLongLong(ctx,argv[1],&myval) == REDISMODULE_OK) {
/* Do something with 'myval' */
}
Accessing Redis keys from modules (访问redis的数据)
Redis module 提供了2套方法来访问redis的数据空间。 按文档中的说法,一套是 high level API ,一套是 low level API 。文档中对于两套API是这么解释的:
low level API :
one is a low level API that provides very fast access and a set of functions to manipulate Redis data structures
In general modules developers should prefer the low level API, because commands implemented using the low level API run at a speed comparable to the speed of native Redis commands.
high level API:
The other API is more high level, and allows to call Redis commands and fetch the result, similarly to how Lua scripts access Redis.
从文档的解释可以看出Redis module 提供了2个层次的API,我们姑且称之为高层API(high level API)和底层API(low level API)。对于高层API,调用起来类似于lua 脚本,而且也较为的方便。但是不如底层API来得快。 同时文档建议开发者在开发模块的时候,尽量调用底层API,因为底层API 的执行速度几乎可以和redis原生命令一样快。
Calling Redis commands (通过high level API 调用 redis命令)
使用high level API 调用 redis命令,都是通过RedisModule_Call这个函数来实现的。 原文文档中给出了一个示例调用incr 命令:
RedisModuleCallReply *reply;
reply = RedisModule_Call(ctx,"INCR","sc",argv[1],"10");
参数解释如下:
- ctx :在之前的部分介绍过,因此不再介绍。
- "INCR":第二个参数是一个C字符串,传递的是要被调用的命令。本例中是incr
- "sc": 第三个参数,指明了接下去传给incr的几个参数,都是些什么类型的。
参数 | 原文说明 |
---|---|
c | Null terminated C string pointer |
b | C buffer, two arguments needed: C string pointer and size_t length. |
s | RedisModuleString as received in argv or by other Redis module APIs returning a RedisModuleString object. |
I | Long long integer. |
v | Array of RedisModuleString objects. |
! | This modifier just tells the function to replicate the command to slaves and AOF. It is ignored from the point of view of arguments parsing. |
- argv[1]和"10":后面的2个参数,就是分别传递给incr的值了。根据上面的"sc"参数可以看出,一个是RedisModuleString 另外一个是C string 。
RedisModule_Call的返回是一个RedisModuleCallReply对象。在两种情况下,返回的是空值(Null):
- 当调用RedisModuleCallReply 错误的时候
- 当redis cluster enabled ,当前访问的key不在这个节点的时候
NULL is returned when the command name is invalid, the format specifier uses characters that are not recognized, or when the command is called with the wrong number of arguments. In the above cases the errno var is set to EINVAL. NULL is also returned when, in an instance with Cluster enabled, the target keys are about non local hash slots. In this case errno is set to EPERM.
Working with RedisModuleCallReply objects. (RedisModuleCallReply的使用)
如前面的部分所述,RedisModuleCallReply 在RedisModule_Call 函数使用的时候被返回给用户。本节介绍下 RedisModuleCallReply的使用。
RedisModuleCallReply会有如下的几个类型:
- REDISMODULE_REPLY_STRING Bulk string or status replies
- REDISMODULE_REPLY_ERROR Errors
- REDISMODULE_REPLY_INTEGER Signed 64 bit integers
- REDISMODULE_REPLY_ARRAY Array of replies
- REDISMODULE_REPLY_NULL NULL reply
对于Strings(REDISMODULE_REPLY_STRING Bulk string or status replies ),error (REDISMODULE_REPLY_ERROR Errors )和Array (REDISMODULE_REPLY_ARRAY Array of replies) 类型,可以通过如下的函数获取返回的长度 :
size_t reply_len = RedisModule_CallReplyLength(reply);
int类型(REDISMODULE_REPLY_INTEGER Signed 64 bit integers ) 获取int的值,可以通过如下的函数:
long long reply_integer_val = RedisModule_CallReplyInteger(reply);
上文的函数,如果传入的参数类型错误,则返回的是 LLONG_MIN。
对于Array类型的返回,获取返回里的某个元素,可以使用如下的函数:
RedisModuleCallReply *subreply;
subreply = RedisModule_CallReplyArrayElement(reply,idx);
如果数组越界了,则返回的是Null。
同时如果需要Strings和error 的原始的C字符串返回,可以像如下的示例一样:
size_t len;
char *ptr = RedisModule_CallReplyStringPtr(reply,&len);
如果入参的reply的类型不是 string或者error ,则返回值是Null。
RedisCallReply也可以转换为 RedisModuleString :
RedisModuleString *mystr = RedisModule_CreateStringFromCallReply(myreply);
如果入参reply不是正确的type,则返回是Null。
对于Reply objects 可以通过RedisModule_FreeCallReply函数进行释放。对于数组类型的返回,不需要挨个去释放元素。此外,如果使用了自动内存管理特性的情况下,开发者不必去释放Reply objects。
Returning values from Redis commands (这节主要介绍使用什么方法将返回值返回给客户端)
大部分情况下,module中新设计的命令都需要返回一定的值给客户端,就像正常的Redis命令一样。 Redis module中提供这个能力的函数簇是 RedisModule_ReplyWith.
返回error
返回一个error 可以使用如下的命令:
RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);
比如开发者可以返回一个"ERR invalid arguments" 的提示:
RedisModule_ReplyWithError(ctx,"ERR invalid arguments");
返回一个整形
返回一个整形可以通过如下的函数 :
RedisModule_ReplyWithLongLong(ctx,12345);
返回一个简单的字符串
返回一个简单的 不是二进制安全或者不带换行的字符串,例如"OK"这种,可以用如下的命令:
RedisModule_ReplyWithSimpleString(ctx,"OK");
如果需要返回一些复杂的字符串,例如需要二进制安全的特性的,可以使用如下的2个函数 :
int RedisModule_ReplyWithStringBuffer(RedisModuleCtx *ctx, const char *buf, size_t len);
int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);
返回一个数组类型
返回值也可以是一个数组。文档中给出了示例如下,首先给出将要返回的数组长度,然后调用相应数量的上面那些回应的API:
RedisModule_ReplyWithArray(ctx,2);
RedisModule_ReplyWithStringBuffer(ctx,"age",3);
RedisModule_ReplyWithLongLong(ctx,22);
同样,回应的数组中的某个元素也可以是一个数组如下:
RedisModule_ReplyWithArray(ctx,2);
RedisModule_ReplyWithStringBuffer(ctx,"age",3);
RedisModule_ReplyWithArray(ctx,3);
RedisModule_ReplyWithLongLong(ctx,22);
RedisModule_ReplyWithLongLong(ctx,22);
RedisModule_ReplyWithLongLong(ctx,22);
返回动态长度的数组
有时想预先知道一个要操作的数组长度是不可能的,列如当我们用Redis module 实现一个FACTOR的命令,它的参数是一个数组结果输出是它的素数因子。为了实现上面的功能,可以先通过一个特殊的参数创建数组(RedisModule_ReplyWithArray(ctx, long )),然后在执行完相应的操作后,最后再设置这个数组的长度。
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
RedisModule_ReplySetArrayLength(ctx, number_of_items);
使用的例子如下:
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
number_of_factors = 0;
while(still_factors) {
RedisModule_ReplyWithLongLong(ctx, some_factor);
number_of_factors++;
}
RedisModule_ReplySetArrayLength(ctx, number_of_factors);
遍历元素然后按条件过滤一部分再返回结果也是这个特性经常使用到的列子。向前文讲的返回一个内嵌数组也是可以的,对于内嵌数组来讲,SetArrayLength函数只会设置上一个最近调用 ReplywithArray的数组的大小。
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
... generate 100 elements ...
RedisModule_ReplyWithArray(ctx, REDISMODULE_POSTPONED_ARRAY_LEN);
... generate 10 elements ...
RedisModule_ReplySetArrayLength(ctx, 10);
RedisModule_ReplySetArrayLength(ctx, 100);
Arity and type checks (参数个数和类型的检查)
如果参数的个数不符合检查的规则,可以使用RedisModule_WrongArity()作为返回:
if (argc != 2) return RedisModule_WrongArity(ctx);
对于打开的key 类型的检查,则可以类似于如下的代码段:
RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
REDISMODULE_READ|REDISMODULE_WRITE);
int keytype = RedisModule_KeyType(key);
if (keytype != REDISMODULE_KEYTYPE_STRING &&
keytype != REDISMODULE_KEYTYPE_EMPTY)
{
RedisModule_CloseKey(key);
return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
}
Low level access to keys(通过low level api 访问key)
通过low level api 去访问Redis的key ,可以拥有和redis原生命令一样的速度和性能。 有几个地方在原文文档中提示需要严格注意的:
- 一个key被打开多次,并且其中至少有一个实例是写入状态,这种情况下可能会导致崩溃。
Opening the same key multiple times where at least one instance is opened for writing, is undefined and may lead to crashes.
- 如果要打开一个key ,最好的途径还是通过low level key API 的方式打开。例如当打开了一个key,之后使用RedisModule_Call的方式去Del这个key,将会导致crash。因此,最安全的方式还是通过low level API 去打开,关闭,操作和管理一个key 。
While a key is open, it should only be accessed via the low level key API. For example opening a key, then calling DEL on the same key using the RedisModule_Call() API will result into a crash. However it is safe to open a key, perform some operation with the low level API, closing it, then using other APIs to manage the same key, and later opening it again to do some more work.
打开和关闭一个key
打开一个Redis的key可以通过如下的函数实现 :
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_READ);
参数 :
- ctx : 之前介绍过了,这里不复述。
- argv[1]: 这里传入的是key的名称。 这个参数必须是 RedisModuleString类型。
- REDISMODULE_READ : 第三个参数很容易理解,让我偷懒下,直接贴原文的解释了:
The third argument is the mode: REDISMODULE_READ or REDISMODULE_WRITE. It is possible to use | to bitwise OR the two modes to open the key in both modes.Currently a key opened for writing can also be accessed for reading but this is to be considered an implementation detail. The right mode should be used in sane modules.
这里还有3个地方注意下:
- 如果打开一个不存在的key为了去写入它,则函数会创建一个新的key。之后可以执行写入的工作 。
- 如果打开一个不存在的key只是为了读取,RedisModule_OpenKey会返回Null 。
- 一个key如果被打开了,可以通过RedisModule_CloseKey函数关闭这个key。当前如果使用了automatic memory management这个功能,也不是需要都主动关闭key 。
获取一个key的类型
获取一个key的类型,可以使用如下的函数 :
int keytype = RedisModule_KeyType(key);
返回值有如下的类型:
- REDISMODULE_KEYTYPE_EMPTY
- REDISMODULE_KEYTYPE_STRING
- REDISMODULE_KEYTYPE_LIST
- REDISMODULE_KEYTYPE_HASH
- REDISMODULE_KEYTYPE_SET
- REDISMODULE_KEYTYPE_ZSET
上述的类型是Redis中常见的类型,还有一个类型是空类型。如果函数返回的是空类型,则代表这个key不存在。
创建一个新的key
创建一个新的key,可以通过如下的示例:
RedisModuleKey *key;
key = RedisModule_OpenKey(ctx,argv[1],REDISMODULE_WRITE);
if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {
RedisModule_StringSet(key,argv[2]);
}
删除一个key
删除一个key可以用如下的函数:
RedisModule_DeleteKey(key);
如果一个key没有被打开并写入,则函数返回REDISMODULE_ERR 。
过期时间的设定(TTLS)
Redis提供了如下2个函数来操作过期时间:
mstime_t RedisModule_GetExpire(RedisModuleKey *key);
int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire);
RedisModule_GetExpire 会返回一个key的过期时间(单位是毫秒)。如果一个key没有过期时间,则会返回REDISMODULE_NO_EXPIRE 。
如果要改变一个key的过期时间,则使用RedisModule_SetExpire函数。如果传入函数的key不存在,则返回REDISMODULE_ERR。
当RedisModule_SetExpire函数被使用,如果一个key当前没有过期时间,则会赋予它一个新的过期时间。如果当前key已经有过期时间,则replaced with the new value 。 如果一个key有过期时间,REDISMODULE_NO_EXPIRE被使用,则过期时间被移除。
获取values 的长度
Redis提供了一个单独的函数获取一个key的value的长度。如果是value的类型是string 则返回字符串长度。如果是list,set,sorted set,hash则返回元素的个数。
size_t len = RedisModule_ValueLength(key);
如果key不存在,则返回0 。
字符串相关的API
对一个key设定string的value ,可以用如下的函数:
int RedisModule_StringSet(RedisModuleKey *key, RedisModuleString *str);
这个函数类似于原生的set命令,如果之前有值存在,则老的值会被覆盖。
获取一个key的value也可以通过DMA(direct memory access)的方式,例如如下的示例:
size_t len, j;
char *myptr = RedisModule_StringDMA(key,&len,REDISMODULE_WRITE);
for (j = 0; j < len; j++) myptr[j] = 'A';
这个示例通过RedisModule_StringDMA直接获取了这个key的values的C原始字符串,然后直接修改了它。在这个示例里面,如果要写入字符串,必须确保当前是WRITE mode 。
有时候,需要更改一个字符串的长度,可以使用如下的函数:
RedisModule_StringTruncate(mykey,1024);
这个函数根据需要截断或放大字符串,如果前一个长度小于我们请求的新长度,则用零字节填充字符串。如果该字符串不存在,则会创建一个字符串值并与该键相关联。注意,每次调用StringTruncate时,都需要重新获取DMA指针,因为旧的指针可能无效。
list 相关API
对于list的操作,可以使用如下的两个函数:
int RedisModule_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele);
RedisModuleString *RedisModule_ListPop(RedisModuleKey *key, int where);
在这两个API中,where参数使用以下宏指定是从尾部还是头部推还是弹出:
- REDISMODULE_LIST_HEAD
- REDISMODULE_LIST_TAIL
sort set 相关API
那啥,原文文档说Documentation missing, please refer to the top comments inside module.c for the following functions。 在此我列下 API吧。
- RedisModule_ZsetAdd
- RedisModule_ZsetIncrby
- RedisModule_ZsetScore
- RedisModule_ZsetRem
- RedisModule_ZsetRangeStop
- RedisModule_ZsetFirstInScoreRange
- RedisModule_ZsetLastInScoreRange
- RedisModule_ZsetFirstInLexRange
- RedisModule_ZsetLastInLexRange
- RedisModule_ZsetRangeCurrentElement
- RedisModule_ZsetRangeNext
- RedisModule_ZsetRangePrev
- RedisModule_ZsetRangeEndReached
hash 相关API
同样的hash相关的API文档也是missing。
- RedisModule_HashSet
- RedisModule_HashGet
Replicating commands
Redis module 也提供了对于AOF和salve同步的支持功能。 在文档中,如果想要命令被replication 有以下的几种方式:
- 在RedisModule_Call的时候在format specifier中加一个! 号。例如:
reply = RedisModule_Call(ctx,"INCR","!sc",argv[1],"10");
- 如果使用的是low level api ,则需要调用RedisModule_ReplicateVerbatim :
RedisModule_ReplicateVerbatim(ctx);
- 还有一种方法是调用RedisModule_Replicate
RedisModule_Replicate(ctx,"INCRBY","cl","foo",my_increment);
Automatic memory management
在前面的内容中,一直提到一个内容就是自动内存管理。在此做下介绍。通常,当用C语言编写程序时,需要手动管理内存。这就是为什么Redis模块API具有释放字符串、关闭打开的密钥、释放响应等功能。但是,由于命令是在一个包含的环境中执行的,并且有一组严格的API,因此Redis能够以一定的性能(大多数情况下,成本非常低)为模块提供自动内存管理:
RedisModule_AutoMemory(ctx);
Allocating memory into modules
普通的C程序使用malloc()和free()来动态分配和释放内存。虽然在redis模块中使用malloc在技术上并不是禁止的,但是最好使用redis模块特定的功能,这些功能是malloc、free、realloc和strdup的精确替代。这些功能包括:
void *RedisModule_Alloc(size_t bytes);
void* RedisModule_Realloc(void *ptr, size_t bytes);
void RedisModule_Free(void *ptr);
void RedisModule_Calloc(size_t nmemb, size_t size);
char *RedisModule_Strdup(const char *str);
这些函数工作方式与libc等价调用完全相同,但它们使用的是和Redis相同的分配器,在调用这些函数的时候,相关的内存分配信息会被记录到Redis的info信息中。如果是使用libc malloc 这些信息都不会被记录。使用模块函数来分配内存的另一个原因是,在模块内创建本机数据类型时,RDB加载函数可以(从RDB文件)直接返回反序列化字符串作为redis module_alloc()分配,这样它们可以在加载后直接用于填充数据结构,而不必复制它们的数据结构。
结尾
看完和翻译完redis的文档,还是有点小期待的。毕竟可以有这么一个棒棒的功能,可以随时扩展Redis的功能。但是也有一些不完美的地方,例如对于Redis cluster 的支持,文档中没有任何的说明,要自己去探索。下一篇文章将会实操一次,编写一个mini的插件,同时探索下module插件如何获得cluster 集群能力的支持。
作者:bush2582
mail:bush2582@163.com
redis module 学习—官网文档整理的更多相关文章
- redis过期机制(官网文档总结)
官网地址:https://redis.io/commands/expire redis过期定义如下: Set a timeout on key. After the timeout has expir ...
- Unity shader 官网文档全方位学习(一)
转载:https://my.oschina.net/u/138823/blog/181131 摘要: 这篇文章主要介绍Surface Shaders基础及Examples详尽解析 What?? Sha ...
- Spring Security 官网文档学习
文章目录 通过`maven`向普通的`WEB`项目中引入`spring security` 配置 `spring security` `configure(HttpSecurity)` 方法 自定义U ...
- Hortonworks官网文档怎么找?
Hortonworks官网文档怎么找? 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 俗话说,授人予鱼不如授人予渔,网上部署HDP的部署方式的博客有很多,看得你是眼花缭乱的.其实万 ...
- 【VR】Leap Motion 官网文档 FingerModel (手指模型)
前言: 感谢关注和支持这个Leap Motion系列翻译的朋友们,非常抱歉因为工作原因非常久没有更新,今后这个翻译还会继续(除非官方直接给出中文文档).本篇献给大家的是 <FingerModel ...
- mybatis官网文档mybatis_doc
在平时的学习中,我们可以去参考官网的文档来学习,这个文档有中文的,方便我们去阅读,而且这里的分类很详细. 官网文档链接:http://www.mybatis.org/mybatis-3/zh/inde ...
- 你会阅读appium官网文档吗
高效学习appium第一步,学会查看appium官方文档.如果能把appium文档都通读一遍,对学习appium大有益处. 而能做到通读appium官方文档的人,想必不是很多,刚开始学习appium的 ...
- 部署openstack的官网文档解读mysql的配置文件
部署openstack的官网文档解读mysql的配置文件(使用与ubutu和centos7等系统) author:headsen chen 2017-10-12 16:57:11 个人原创,严禁转载 ...
- (五)Spring Boot官网文档学习
文章目录 SpringApplication SpringApplication 事件 `ApplicationContext ` 类型 访问传递给 `SpringApplication` 的参数 A ...
随机推荐
- asp .net core 使用spa
要求 .net core 2.1 引用包 Microsoft.AspNetCore.SpaServices 先在angular目录下执行 npm i npm run build 关键代码 servic ...
- ios 调用系统发短信以及打电话功能
先介绍一种最简单的方法: 调用打电话功能 [[UIApplicationsharedApplication] openURL:[NSURL URLWithString:@"tel://100 ...
- 芯片超Intel,盈利比肩Apple,三星成科技界"全民公敌"
原标题:芯片超英特尔,盈利比肩苹果:三星现在是科技界“全民公敌”了 当人们津津乐道于三星的手机业务或者是电视业务时,它已静悄悄的拿下了芯片行业的第一,并且凭借着在芯片上的巨大获利让它的老对手们眼红 ...
- c# Unity依赖注入WebService
1.IOC与DI简介 IOC全称是Inversion Of Control(控制反转),不是一种技术,只是一种思想,一个重要的面相对象编程的法则,它能知道我们如何设计出松耦合,更优良的程序.传统应用程 ...
- Asp +Js 无刷新分页
Default.aspx代码 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind=" ...
- 解决win10开机出现recovery there was a problem with a device connected to your pc
问题描述: 开机无限重启并提示 recovery there was a problem with a device connected to your PC An unexpected I/O er ...
- C# ACCESS 向含有自动编码字段表中添加记录提示“查询值的数目与目标字段中的数目不同”
引发错误的SQL语句如下: sqlStr = "insert into tb_ReportLog values('" + DevSite + "','" + D ...
- Codility--- Triangle
Task description A zero-indexed array A consisting of N integers is given. A triplet (P, Q, R) is tr ...
- [迟到的万圣节向]可怕的python
什么?python简单易懂好学可读性高灵活耐用扩展好? 预测下面几个小段落的输出,来看看这个能过几关? ============================ Stage 1 预测下列输出 def ...
- sentinel 核心概念
编者注:前段时间笔者在团队内部分享了sentinel原理设计与实现,主要讲解了sentinel基础概念和工作原理,工作原理部分大家听了基本都了解了,但是对于sentinel的几个概念及其之间的关系还有 ...