Lua:给Redis用户的入门指导
可能你已经听说过Redis中嵌入了脚本语言,但是你还没有亲自去尝试吧?
这个入门教程会让你学会在你的Redis 服务器上使用强大的lua语言。
Hello, Lua!
我们的第一个Redis Lua 脚本仅仅返回一个字符串,而不会去与redis 以任何有意义的方式交互。
local msg = "Hello, world!"
return msg
这是非常简单的,第一行代码定义了一个本地变量msg存储我们的信息,第二行代码表示从redis服务端返回msg的值给客户端。保存这个文件到hello.lua,像这样去运行:
redis-cli EVAL "$(cat hello.lua)" 0
运行这段代码会打印"Hello,World!", EVAL在第一个参数是我们的lua脚本,这我们用cat命令从文件中读取我们的脚本内容。第二个参数是这个脚本需要访问的Redis的键的数字号。我们简单的"Hello Script"不会访问任何键,所以我们使用0
访问键和参数
假设我们要建立一个URL简写服务器。我们就要去存储每条进入的URL并返回一个唯一数值,以便以后通过这个数值访问到该URL。
我们将利用Lua脚本立即从Redis中用INCRand获取一个唯一标识ID,以这个标识ID作为URL存储于一个哈希中的键值:
local link_id = redis.call("INCR", KEY[1])
redis.call("HSET", KEYS[2], link_id, ARGV[1])
return link_id
我们将用call()函数首次访问Redis。call()的参数就是发给Redis的命令:首先INCR <key>,然后HSET <key> <field> <value>。这两个命令将依次执行----当这个脚本执行时,Redis不会做任何事,它将非常快地运行。
我们将会访问两个Lua表:KEYS和ARGV。表单式关联性数组和结构化数据的Lua唯一机制。对于我们的意图,你可以把它们看做是一个你所熟悉的任意语言对等的数组,但是提醒两个很容易困扰到新手的两个Lua定则:
- 表是基于1的,也就说索引以数值1开始。所以在表中的第一个元素就是mytable[1],第二个就是mytable[2]等等。
- 表中不能有nil值。如果一个操作表中有[1, nil, 3, 4],那么结果将会是[1] ----表将会在第一个nil截断。
当调用这个脚本时,我们还需要传递KEYS和ARGV表的值:
redis-cli EVAL "$(cat incr-and-stor.lua)" 2 links:counter links:urls http://malcolmgladwellbookgenerator.com/
在EVAL语句中,2指出需要传入的KEY的个数,后面跟着需要传入的两个KEY,最后传入的是ARGV的值。在Redis中执行Lua脚本时,Redis-cli会检查传入KEY的个数,除非传入的完全是命令。
为了解释得更清楚,下面列出替换KEY和ARGV后的脚本:
local link_id = redis.call("INCR", "links:counter")
redis.call("HSET", "links:urls", link_id, "http://malcolmgladwellbookgenerator.com")
return link_id
为Redis编写Lua脚本时,每个KEY都是通过KEYS表指定。ARGV表用来传递参数,这个例子中ARGV用来传入URL。
逻辑条件:increx与hincrex
上一个例子保存链接短网址,想要知道这个链接的点击次数,在Redis中添加一个hash计数器。当带有链接标记的用户访问时,我们检查其是否存在,如存在则需要给计数器加1:
if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
return redis.call("HINCR", KEYS[1], ARGV[1])
else
return nil
end
每次有人点击短网址,我们运行这个脚本跟踪这个链接被再次分享。我们用EVAL来调用脚本,传入inlinks:visits(keys[1])和上一个脚本返回的链接标识(ARGV[1])。
这段脚本将检查是否存在相同的hash,如果存在就为这个标准的Redis KEY加1.
if redis.call("EXISTS", KEYS[1]) == 1 then
return redis.call("INCR", KEYS[1])
else
return nil
end
脚本加载与注册执行
注意,当Redis在运行lua脚本的时候,其他的事情什么都干不了!脚本最好只是简单的扩展Redis进行较小的原子操作和简单的逻辑控制需要,Lua脚本中的bug可能引发整个Redis服务器锁---最好保持脚本的简短和易于调试。
虽然这些脚本一般都比较短小,但我们还是希望不要每次执行时都使用完整的Lua脚本,实际上可以在程序一步一步(译注:application boots翻译有难度)开发中注册Lua脚本(或者在你部署时注册),然后用注册后生成的SHA-1标识来进行调用。
redis-cli SCRIPT LOAD "return 'hello world'"
=> "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
redis-cli EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0 => "hello world"
显示调用SCRIPT LOAD通常是不必要的,当一个程序执行EVAL时就已隐式加载了。程序会先尝试EAVALSHA,当脚本没有找到时会调用EVAL。
对于Ruby开发者,可以看一下Shopify’s Wolverine,其可以为Ruby应用简单的加载并存储Lua脚本。对于PHP开发者,Predis 支持加载Lua脚本作为普通Redis命令进行调用(译注:需要继承Predis\Command\ScriptedCommand基类,并注册命令)。如果你使用这些或者其它的工具来标准化与Lua的交互,请让我知道,我很感兴趣知道本文之外的内容。
何时使用Lua
Redis支持WATCH/MULTI/EXEC这样的块,能进行一组操作,也能一起提交执行,看起来与Lua有重叠。应该如何进行选择?MULT块中所有操作独立,但在Lua中,后面的操作能依赖前面操作的执行结果。同时使用Lua脚本还能够避免WATCH使用后竞争条件引起客户端反应变慢的情况。
在RedisGreen(译注:国外一家专门提供Redis主机的服务商),我们看到许多应用使用Lua的同时也使用MULTI/EXEC,但两者但不是替代关系。许多成功的Lua脚本都很小,仅仅实现一个你的应用需要而Redis命令中没有单一的功能。
访问库
Redis的Lua解释器加载七个库:base, table, string, math, debug, cjson和cmsgpack。前几个都是
标准库,充许你使用任何语言进行基本的操作。后面两个可以让Redis支持JSON和MessagePack—这是非常有用的功能,同时我也很想知道为什么常常看不到这种用法。
Web应用程序常常使用JSON作为api返回数据,你也许也可以把一堆JSON数据存到Redis的key中。当想访问某些JSON数据时,首先需要保存到一个hash中,使用Redis的JSON支持将非常方便:
if redis.call("EXISTS", KEYS[1]) == 1 then
local payload = redis.call("GET", KEYS[1])
return cjson.decode(payload)[ARGV[1])
else
return nil
end
在这里我们检查看key是否存在,如不存在则快速返回nil。如存在则从Redis中获取JSON值,用cjson.decode()进行解析,然后返回请求内容。
redis-cli set apple '{ "color": "red", "type": "fruit" }'
=> OK
redis-cli eval "${cat json-get.lua)" 1 apple type
=> "fruit"
加载这段脚本进你的Redis服务器,将JSON数据保存到Redis中,通常是hash。 虽然我们每次访问时都必须解析,但只要你的对象很小,这个操作实际上是非常快的。
如果你的API只是在内部提供,通常需要考虑效率上的问题,MessagePack 是比采用JSON更好的选择,它更小,更快,在Redis(更多场合也是如此),MessagePack是JSON更好的替代品。
if redis.call("EXISTS", KEYS[1]) == 1 then
local payload = redis.call("GET", KEYS[1])
return cmsgpack.unpack(payload)[ARGV[1])
else
return nil
end
数值转换
Lua和Redis各有自己的一套类型,因此,理解Redis与Lua在边界调用相互转换引起值的改变是非常重要的。一个来自Lua中number返回到Redis客户端时变成了integer—任何数字后面的小数点都被清除了:
local indiana_pi = 3.2 return indiana_pi
在你运行这段脚本时,Redis将返回一个整数3,丢失了pi中有用的片段。看起来很简单,但是一旦开始进行Redis与中间脚本交互时就需要更小心。例如:
local indiana_pi = 3.2 redis.call("SET", "pi", indiana_pi) return redis.call("GET", "pi")
执行的结果是一个字符串:“3.2”,这是为什么呢?在Redis中没有专有的数值类型,当我们第一次调用SET的时候,Redis就已经将它保存为字符串了,将Lua初始化时将其作为一个浮点数的类型信息给丢失了。所以当我们后面取出这个值时,它就变成了一个字符串。
在Redis中,除了INCR和DECR,其它的GET,SET操作所访问的数据都作为字符串处理。INCR与DECR是专门对数值的操作,实际上返回是整数(integer)回复(维护和存储遵守数字规则),但Redis内部保存类型实际上还是字符串值。
总结:
下面这些都是在Redis中使用Lua时常见的错误:
- 表是Lua中的表达式,与很多流行语言不同。KEYS中的第一个元素是KEYS[1],第二个是KEYS[2](译注:不是0开始)
- nil是表的结束符,[1,2,nil,3]将自动变为[1,2],因此在表中不要使用nil。
- redis.call会触发Lua中的异常,redis.pcall将自动捕获所有能检测到的错误并以表的形式返回错误内容。
- Lua数字都将被转换为整数,发给Redis的小数点会丢失,返回前把它们转换成字符串类型。
- 确保在Lua中使用的所有KEY都在KEY表中,否则在将来的Redis版中你的脚本都有不能被很好支持的危险。
- Lua脚本和其它Redis操作一样,在脚本执行时,其它的一切都不能运行。考虑用脚本来护展Redis服务器能力,但要保持短小和有用。
补充读物
下面有许多关于Lua和Redis很好的在线资源,本文只是我所用到的很少一部分:
- Lua Reference Manual
- Lua Tutorial Directory
- EVAL Docs
- evalsha.com — 偶尔会有垃圾邮件,但内容很好(译注:里面有很多的Lua脚本,以EVALSHA方式提供,超棒,希望对你有用。)
Lua:给Redis用户的入门指导相关推荐
- Lua: 给 Redis 用户的入门指导
可能你已经听说过Redis 中嵌入了脚本语言,但是你还没有亲自去尝试吧? 这个入门教程会让你学会在你的Redis 服务器上使用强大的lua语言. Hello, Lua! 我们的第一个Redis Lu ...
- (转) Lua: 给 Redis 用户的入门指导
可能你已经听说过Redis 中嵌入了脚本语言,但是你还没有亲自去尝试吧? 这个入门教程会让你学会在你的Redis 服务器上使用强大的lua语言. Hello, Lua! 我们的第一个Redis Lu ...
- Redis框架从入门到学精(全)
目录 前言 1.NoSQL 1.1 其他数据库 2.Redis 2.1 软件安装 2.2 数据类型 2.2.1 key值键位 2.2.2 string字符串 2.2.3 list列表 2.2.4 se ...
- 史上最萌最认真的机器学习/深度学习/模式识别入门指导手册(一)
喵喵喵~大家元宵节快乐噢.有没有要陪小夕出去看烟花的吖... 小夕借此给热爱学习的喵喵们献上这篇拙文,希望不要嫌弃哦~ 还有,小夕画的封面图是不是很棒呀( ̄∇ ̄) 小夕发现现在想进军人工智能领域的程序 ...
- mdin偏移_C8051F020入门指导重点.ppt
C8051F020入门指导重点 4.4 看门狗定时器复位 MCU 内部有一个使用系统时钟的可编程看门狗定时器(WDT).当看门狗定时器溢出时,WDT 将强制CPU 进入复位状态. 为了防止复位,必须在 ...
- 2.写给设计师看的HTMLCSS入门指导
转自:http://www.uisdc.com/html-and-css-guide 整体简介 在开始学习HTML&CSS之前,首先要搞清楚两者的区别.两者在整体上有着很明显的差异. 整体看来 ...
- Zigbee入门指导(二)
Zigbee入门指导(二) 2010年08月06日 Zigbee入门指导(二) ――运行Zigbee例程 logiclimit 在Zigbee入门指导(一)中讲解了基于CC2430的Zigbee开发环 ...
- Zigbee入门指导(二)mdash;mdash;运行Zigbee例程
Zigbee入门指导(二) --运行Zigbee例程 logiclimit 在Zigbee入门指导(一)中讲解了基于CC2430的Zigbee开发环境的搭建,安装完Ti的协议栈后,里面有多个例程,帮助 ...
- 《用户至上:用户研究方法与实践》用户体验入门
本节书摘来自华章出版社<用户至上:用户研究方法与实践>一书中的第1章,第1节,作者凯茜·巴克斯特(Kathy Baxter)[美] 凯瑟琳·卡里(Catherine Courage)凯莉· ...
最新文章
- 计算机应用基础网络统考操作,全国网络统考《计算机应用基础》完整最新题库及答案[整理].pdf...
- 利用Httponly提升web应用程序安全性
- 漫谈C++ Builder多线程编程技术
- 一个打印螺旋数的程序
- Oracle学习:子查询 (sql 嵌套 sql)
- Center os vi
- 如何使用代码获得ABAP repository object不同版本的内容
- Android KeyStore流程
- Symfony 4.2.4 和 3.4.23 发布,经典 PHP Web 开发框架
- 抖音公布“美好音符年”活动数据:这个省的用户最好运
- Kinect 开发 —— 面部追踪
- Xml中SelectSingleNode方法,xpath查找某节点用法
- 2018最新电大网考计算机,2018年最新电大网考计算机应用基础统考试题.doc
- 二、VS插件之VassistX
- Linux SPI驱动(Linux驱动开发篇)
- 聊聊旷厂黑科技 | 更真切感受影像世界的美好,旷视实时双超AI算法还原你的“夏日回忆”...
- 【OpenGL】笔记二十一、Alpha测试、混合测试
- 游戏阴阳师启动闪退问题解决
- Oracle12C--触发器(52)
- JS如何调用Android原生方法
热门文章
- Java实现心跳机制
- springmvc 注解总结
- Effective Java之对可恢复的情况使用受检异常,对编程错误使用运行时异常(五十八)
- ActiveMQ实战篇之 java和spring xml创建Broker(一)
- hadoop2.6.0安装详细步骤
- Leecode 21. 合并两个有序链表——Leecode大厂热题100道系列
- KeyError: 报错_python系列学习笔记
- Web前端开发笔记——第三章 CSS语言 第四节 CSS列表、表格样式
- MongoDB数据库(了解MongoDB及基础命令,备份数据库)
- 多维数组的索引与切片_Numpy库使用入门(二)数据的索引和切片