上下文:
Redis 4.0 的RC终于出来了, 看了redis官方及antirez的博客都有提到4.0的各种新功能,对我来说是相当有吸引力的 。
估计有人好奇现在不是才redis 3 .x么, 怎么直接干到redis 4.0 大版本了,据antirez说, redis 3.x 主要是集群功能, redis 4.0 是个全新的大跃进 ( 当然我自己加工了下 ,哈哈) 。
另外蛋疼的找了台大内存的服务器,使用以前的golang redis压力测试脚本, 综合测了下redis各数据结构的读写性能, 结果跟现在的redis 3.4 没啥差异的,看来4.0 真的就是功能上的功能。
该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.
我们这里可以简单说下 redis 4.0 的新功能:
1. 自定义扩展redis的模块, 当然相比 redis lua脚本来说,还是有些学习成本的…
2. psync2 他的好处在于 redis 主从切换后,不需要重新进行 重新fullsync同步,只需要部分同步,有点类似binlog那种. 具体实现还没看,阿里自己是通过timestamp时间戳实现的.
3. memory 指令可以估算内容大小
4. 非数据命令的异步执行,我们知道redis处理请求是单线程的,执行数据偏大的flush及del会阻塞的,redis 4.0 加了异步操作的功能…. 很好奇他的实现.
等等 …
我自己最关注Redis 4.0 新版加入了自定义模块开发,其实以前在unstable的版本时 redis 就支持社区的自定义模块了, 只是一直游离在外,很多人不关注不知道而已。 Redis 开放自定义模块是大势所趋呀, 我们只需要按照扩展的样例代码撸就可以了,不用细心琢磨redis那内部的密集代码了。
在redis 2.x的时候,作者加入了lua的解释器,但因为种种的安全限制,只能用那固定几个库包,但如果是应对频繁网络IO的场景, redis lua 绝逼是够用了… 有兴趣可以看看.
首先我们要安装redis 4.0 rc 新版. antirez说过,在3.x版本中不会加入自定义模块加载的.
#xiaorui.cc wget https://github.com/antirez/redis/archive/4.0-rc2.tar.gz tar zxvf 4.0* cd 4.0 make make test
下载第三方的Redis modules模块,make编译
git clone https://github.com/RedisLabsModules/redex cd redex make
上面编译完了后,在src下是有一堆动态链接库so 文件的, 然后我们修改redis.conf的配置, 加入这些so文件。
--loadmodule /path/src/module.so
Redis模块源码在这里. https://github.com/RedisLabsModules/redex , 这名字有点意思哈…. redex vs redis ?
redisEx moudules的扩展基本是 在redis本身数据结构基础上做的调整 .
Included modules: rxkeys - extended keys commands (Module Hub page) rxstrings - extended Strings commands (Module Hub page) rxhashes - extended Hashes commands (Module Hub page) rxlists - extended Lists commands (Module Hub page) rxsets - extended Sets commands (Module Hub page) rxzsets - extended Sorted Sets commands (Module Hub page) rxgeo - extended Geo Sets commands (Module Hub page)
我们看下RedisEx对 zset的相关调整,比如里面的zpop 和 zrevpop , 一看这名字我觉得大家就能感觉出来他是干嘛的吧?
redis 本身的zset 是没有pop命令的,如果你想获取并删除,那么就只能 zrange and zrem . zpop 是帮你pop出来一个最小score的数据,zrevpop 反之.
# xiaorui.cc int ZPopGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 2 || argc > 3) { return RedisModule_WrongArity(ctx); } RedisModule_AutoMemory(ctx); /* Get the target command. */ size_t cmdlen; const char *cmd = RedisModule_StringPtrLen(argv[0], &cmdlen); int rev = !strncasecmp("zrevpop", cmd, cmdlen); int withscore = RMUtil_ArgExists("WITHSCORE", argv, argc, 2); if ((argc == 3) && !withscore) return RedisModule_WrongArity(ctx); // open the key and make sure it's indeed a ZSET and not empty RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE); if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) { // and empty key - return null if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) { RedisModule_ReplyWithNull(ctx); return REDISMODULE_OK; } // 类型错误,抛出异常 RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } // 获取最小score分值的元素 double score; (rev ? RedisModule_ZsetLastInScoreRange : RedisModule_ZsetFirstInScoreRange)( key, REDISMODULE_NEGATIVE_INFINITE, REDISMODULE_POSITIVE_INFINITE, 0, 0); RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key, &score); RedisModule_ZsetRangeStop(key); // 删除这个元素 RedisModule_ZsetRem(key, ele, NULL); // 返回元素 或者 含有分值 (取决于是否传入withscore参数) RedisModule_ReplyWithArray(ctx, (withscore ? 2 : 1)); RedisModule_ReplyWithString(ctx, ele); if (withscore) RedisModule_ReplyWithDouble(ctx, score); return REDISMODULE_OK; }
Redis set里多了一个 msismember的命令扩展,是个sismember的多扩展 . 用来判断多个集合里是否含有某个元素, 符合一个条件加 +1 , 如果返回 2 ,那么就是两个集合都含有这个元素.
redis> SADD admins Alice Bob xiaorui.cc (integer) 3 redis> SADD oncall Zoe Bob Xavier (integer) 3 redis> MSISMEMBER admins oncall Alice (integer) 1 redis> MSISMEMBER admins oncall Zoe (integer) 1 redis> MSISMEMBER admins oncall Bob (integer) 2
Reids Msismember 命令源代码如下 :
#xiaorui.cc int MSIsMemberCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 3) { if (RedisModule_IsKeysPositionRequest(ctx)) return REDISMODULE_OK; else return RedisModule_WrongArity(ctx); } if (RedisModule_IsKeysPositionRequest(ctx)) { size_t i; for (i = 1; i < argc - 1; i++) RedisModule_KeyAtPos(ctx, i); return REDISMODULE_OK; } RedisModule_AutoMemory(ctx); int iele = argc - 1; size_t count = 0; int i; for (i = 1; i < iele; i++) { RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[i], REDISMODULE_READ | REDISMODULE_WRITE); if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) continue; if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_SET) { // 判断数据类型 RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE); return REDISMODULE_ERR; } RedisModuleCallReply *rep = RedisModule_Call(ctx, "SISMEMBER", "ss", argv[i], argv[iele]); // 循环遍历调用 sismember 查看set集合中是否含有该元素 RMUTIL_ASSERT_NOERROR(rep) count += RedisModule_CallReplyInteger(rep); // 如何一个就加一个数 } RedisModule_ReplyWithLongLong(ctx, count); //返回 return REDISMODULE_OK; }
通过上面几个redis扩展源码我们发现,基本都是多个命令合并成一个函数,对于客户端来说节省了网络io的开销,又保证了原子性。
分享个Dvir Volk 的redis 模块开发视频分享,我看了好几遍,受益匪浅,就差动手去开发了…
https://www.youtube.com/watch?v=bQ9MyImCYqc
打个小样, 这两天在发力写个可自增 zset ,敬请关注呀.
END.