Redis基于eval的多字段原子增量计算
目录
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
Redis基于eval的多字段原子增量计算的更多相关文章
- ODI基于源表时间戳字段获取增量数据
实现目标:通过ODI获取一个没有时间戳的子表(qb_bw)的增量数据,而主表(qb_tb)有一个rksj入库时间,且主表和子表之间通过ID关联.目标表名是qb_bw1. 设计原理:通过在ODI的map ...
- Redis之eval+lua实现初步
目录 目录 1 1. 前言 1 2. 执行方式 1 3. 执行过程 3 4. 使用原则 3 1. 前言 Redis的实现保证eval的执行是原子的,即使eval执行的lua超时,Redis也不会自动终 ...
- Particles.js基于Canvas画布创建粒子原子颗粒效果
文章目录 使用方法 自定义参数 相关链接 Particles.js是一款基于HTML5 Canvas画布的轻量级粒子动画插件,可以设置粒子的形状.旋转.分布.颜色等属性,还可以动态添加粒子,效果非常炫 ...
- Redis基于Java的客户端SDK收集
如果要找这类的SDK,第一反应应该直奔官网,找一下看下有什么推荐.先找最权威的回答,找不到再尝试民间方案. 就Redis来说,官方已经提供了一个列表包括市面上绝大多数语言的SDK,可以参考以下网址看J ...
- Others-大数据平台Lambda架构浅析(全量计算+增量计算)
大数据平台Lambda架构浅析(全量计算+增量计算) 2016年12月23日 22:50:53 scuter_victor 阅读数:1642 标签: spark大数据lambda 更多 个人分类: 造 ...
- win7基于mahout推荐之用户相似度计算
http://www.douban.com/note/319219518/?type=like win7基于mahout推荐之用户相似度计算 2013-12-03 09:19:11 事情回到半年 ...
- Atitit.基于时间戳的农历日历历法日期计算
Atitit.基于时间戳的农历日历历法日期计算 1. 农历xx年的大小月份根据万年历查询1 2. 农历xx年1月1日的时间戳获取1 3. 计算当年的时间戳与农历日期的对应表,时间戳为key,日期为va ...
- [Redis] 基于redis的分布式锁
前言分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁. 可靠性首先,为了确保 ...
- Redis 使用 Eval 多个键值自增操作示例
在PHP上使用Redis 给多个键值进行自增,示例如下: $set['money'] = $this->redis->hIncrByFloat($key, $hour .'_money', ...
随机推荐
- Linux下Python2升级Python3
Linux下Python2的升级方法: 一.下载Python3安装包: 1.在线下载 wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2 ...
- sqlserver数据库的物理存储格式和逻辑存储格式
物理存储结构: 数据库文件在磁盘上的存储形式: 主数据文件:*.mdf.用来存储数据库的启动信息.存储部分或全部的数据.整个的数据库只能有一个主数据文件 辅助数据文件:*.ndf.用于存储主数据文件未 ...
- bootstrap切换按钮点击后显示的颜色
点击按钮后将按钮类库切换为值为btn-success ·· test ·· jquery code ·· $(".tag-checkbox").toggleClass(" ...
- c# ?. 空值传播运算符
当左侧为空时不执行右侧代码,避免出现为null的错误,同时也避免了判断是否为null,可以和??一起连用,省了好多事.举例如下: 以前:var res=obj==null?5:obj.a; 现在:va ...
- BOM浏览器对象模型;
1.window.open(url,ways) url是打开的网页地址 ways是打开方式(-blank:-self) 2.window.close()关闭窗口 3.浏览器的用户信息 window.n ...
- c# 关于字典dictionary 按时间排序
上文中说到sortedlist 排序是键排序,不符合项目要求问题,接着使用字典dictionary 对value 为时间按照升序排序,问题解决.中间涉及到linq的使用.还有其他的写法,但发现下边的写 ...
- c# sharpsvn 客户端开发总结
判断文件夹是否是 working copy 工作副本两个方法 if (!IsWorkingCopy(localPath))//查看某目录是否是受svn管理的状态, 即是否为工作副本 { notiny ...
- MSDos
一.简介 二.源码 http://www.computerhistory.org/atchm/microsoft-research-license-agreement-msdos-v1-1-v2- ...
- 可读性很强的C语言的函数指针定义
通常C/C++程序里面要用到大量的指针,其语法非常难以阅读.比如下面的vp指针类型: #include <iostream> using namespace std; typedef vo ...
- samtools
samtools 用法 samtools <command> [options] command 见以下列表, 每个 command 的 options 也不同 dict faidx in ...