前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis基于eval的多字段原子增量计算

Redis基于eval的多字段原子增量计算

作者头像
一见
发布2018-12-06 15:45:41
1.2K0
发布2018-12-06 15:45:41
举报
文章被收录于专栏:蓝天蓝天

1. 前言

一些应用场景需要对多个值进行原子计数,Redis的eval+hincrby可以达到目标,但如果计算的字段比较多时,效率会是个问题,它的时间复杂度为O(N),而且对于查询也同样如此。如果能将所有字段作为一个个struct成员,时间复杂度会固定下来。如果能象C/C++中的引用或指针操作,时间复杂度可以降低到O(1),否则考虑先get再set,这样时间复杂度为O(2),当字段数较多时,比如达到10个甚至更多时,相比O(N)就好了许多。

2. 优点

1)不需要hash,普通kv即可实现多字段的计数,而且是原子操作

2)当字段较多时,性能不会线性下降(hincrby多字段操作性能会线性下降,因为多字段hincrby操作时间复杂度为O(n))

3)当字段较多时,查询性能不变,保持为O(1)

3. 方法一:使用struct

3.1. 设置初始值(覆盖原有的,如果存在)

调用struct的pack函数打包(序列化)两个字段的值56和78,并将该struct赋值给本地变量a(注意Redis内的lua不支持全局变量,如果需要全局变通,可变通使用Redis的KV),然后将变量a作为k1的值设置进去。

127.0.0.1:6379> EVAL 'local a;a=struct.pack("ll",56,78);local x=redis.call("set","k1",a);return x;' 0
OK

3.2. 查询k1的值

因为struct是个二进制值,因为取到值时,需要先unpack反序列化(解包)。

127.0.0.1:6379> eval 'local x=redis.call("get","k1");local m,n=struct.unpack("ll",x);return {m,n}' 0
1) (integer) 56
2) (integer) 78

3.3. 设置初始值(覆盖原有的,如果存在)

127.0.0.1:6379> EVAL 'local a;a=struct.pack("lll",56,78,99);local x=redis.call("set","k1",a);return x;' 0
OK

3.4. 查询k1的值

127.0.0.1:6379> eval 'local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);return {m,n,l}' 0
1) (integer) 56
2) (integer) 78
3) (integer) 99

3.5. 增量操作(增1)

这内含两个Redis操作:get和set两个操作,因此时间复杂度为O(2)。

127.0.0.1:6379> eval 'local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);m=m+1;n=n+1;l=l+1;local a=struct.pack("lll",m,n,l);local z=redis.call("set","k1",a);return z;' 0
OK

3.6. 查询k1的值

127.0.0.1:6379> eval 'local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);return {m,n,l}' 0
1) (integer) 57
2) (integer) 79
3) (integer) 100

3.7. 增量操作(增1)

127.0.0.1:6379> eval 'local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);m=m+1;n=n+1;l=l+1;local a=struct.pack("lll",m,n,l);local z=redis.call("set","k1",a);return z;' 0
OK

3.8. 查询k1的值

127.0.0.1:6379> eval 'local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);return {m,n,l}' 0
1) (integer) 58
2) (integer) 80
3) (integer) 101

3.9. pack和unpack

Redis内置支持struct,pack和unpack中的第一个参数为格式参数,其中单个“l”表示有符号long类型,大写的“L”则表示无符号的long类型,更多可以参见eval命令的说明:https://redis.io/commands/eval

3.10. AOF文件

上述操作对应的AOF文件内容:

$ tail -f appendonly-6379.aof
*2
$6
SELECT
$1
0
*3
$4
EVAL
$76
local a;a=struct.pack("ll",56,78);local x=redis.call("set","k1",a);return x;
$1
0
*3
$4
EVAL
$80
local a;a=struct.pack("lll",56,78,99);local x=redis.call("set","k1",a);return x;
$1
0
*3
$4
eval
$159
local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);m=m+1;n=n+1;l=l+1;local a=struct.pack("lll",m,n,l);local z=redis.call("set","k1",a);return z;
$1
0
*3
$4
eval
$159
local x=redis.call("get","k1");local m,n,l=struct.unpack("lll",x);m=m+1;n=n+1;l=l+1;local a=struct.pack("lll",m,n,l);local z=redis.call("set","k1",a);return z;
$1
0

3.11. 进化的增量操作

可用于生产环境的增量操作,允许被操作的key不存在(大小超过200字节):

eval 'local x=redis.call("get",KEYS[1]); local m,n,l;if (x) then m,n,l=struct.unpack("lll",x);m=m+ARGV[1];n=n+ARGV[2];l=l+ARGV[3]; else m=ARGV[1];n=ARGV[2];l=ARGV[3]; end; local a=struct.pack("lll",m,n,l);local z=redis.call("set",KEYS[1],a);return z;' 1 "k1" 1 2 3

3.12. 进化的查询操作

可用于生产环境的查询操作,允许被查询的key不存在:

eval 'local x=redis.call("get",KEYS[1]); if (x) then local m,n,l=struct.unpack("lll",x);return {m,n,l}; else return x; end;' 1 "k1"

4. 方法二:使用CJSON

暂略。

5. 方法三:使用CMSGPACK

暂略。

6. 方法四:借助Redis的module

6.1. 参考一:rediSQL

https://github.com/RedBeardLab/rediSQL

6.2. 参考二:ReJSON

https://github.com/RedisLabsModules/ReJSON

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-10-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 前言
  • 2. 优点
  • 3. 方法一:使用struct
    • 3.1. 设置初始值(覆盖原有的,如果存在)
      • 3.2. 查询k1的值
        • 3.3. 设置初始值(覆盖原有的,如果存在)
          • 3.4. 查询k1的值
            • 3.5. 增量操作(增1)
              • 3.6. 查询k1的值
                • 3.7. 增量操作(增1)
                  • 3.8. 查询k1的值
                    • 3.9. pack和unpack
                      • 3.10. AOF文件
                        • 3.11. 进化的增量操作
                          • 3.12. 进化的查询操作
                          • 4. 方法二:使用CJSON
                          • 5. 方法三:使用CMSGPACK
                          • 6. 方法四:借助Redis的module
                            • 6.1. 参考一:rediSQL
                              • 6.2. 参考二:ReJSON
                              相关产品与服务
                              云数据库 Redis
                              腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档