可能你已经听说过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用户的入门指导相关推荐

  1. Lua: 给 Redis 用户的入门指导

    可能你已经听说过Redis 中嵌入了脚本语言,但是你还没有亲自去尝试吧?  这个入门教程会让你学会在你的Redis 服务器上使用强大的lua语言. Hello, Lua! 我们的第一个Redis Lu ...

  2. (转) Lua: 给 Redis 用户的入门指导

    可能你已经听说过Redis 中嵌入了脚本语言,但是你还没有亲自去尝试吧?  这个入门教程会让你学会在你的Redis 服务器上使用强大的lua语言. Hello, Lua! 我们的第一个Redis Lu ...

  3. 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 ...

  4. 史上最萌最认真的机器学习/深度学习/模式识别入门指导手册(一)

    喵喵喵~大家元宵节快乐噢.有没有要陪小夕出去看烟花的吖... 小夕借此给热爱学习的喵喵们献上这篇拙文,希望不要嫌弃哦~ 还有,小夕画的封面图是不是很棒呀( ̄∇ ̄) 小夕发现现在想进军人工智能领域的程序 ...

  5. mdin偏移_C8051F020入门指导重点.ppt

    C8051F020入门指导重点 4.4 看门狗定时器复位 MCU 内部有一个使用系统时钟的可编程看门狗定时器(WDT).当看门狗定时器溢出时,WDT 将强制CPU 进入复位状态. 为了防止复位,必须在 ...

  6. 2.写给设计师看的HTMLCSS入门指导

    转自:http://www.uisdc.com/html-and-css-guide 整体简介 在开始学习HTML&CSS之前,首先要搞清楚两者的区别.两者在整体上有着很明显的差异. 整体看来 ...

  7. Zigbee入门指导(二)

    Zigbee入门指导(二) 2010年08月06日 Zigbee入门指导(二) ――运行Zigbee例程 logiclimit 在Zigbee入门指导(一)中讲解了基于CC2430的Zigbee开发环 ...

  8. Zigbee入门指导(二)mdash;mdash;运行Zigbee例程

    Zigbee入门指导(二) --运行Zigbee例程 logiclimit 在Zigbee入门指导(一)中讲解了基于CC2430的Zigbee开发环境的搭建,安装完Ti的协议栈后,里面有多个例程,帮助 ...

  9. 《用户至上:用户研究方法与实践》用户体验入门

    本节书摘来自华章出版社<用户至上:用户研究方法与实践>一书中的第1章,第1节,作者凯茜·巴克斯特(Kathy Baxter)[美] 凯瑟琳·卡里(Catherine Courage)凯莉· ...

最新文章

  1. 计算机应用基础网络统考操作,全国网络统考《计算机应用基础》完整最新题库及答案[整理].pdf...
  2. 利用Httponly提升web应用程序安全性
  3. 漫谈C++ Builder多线程编程技术
  4. 一个打印螺旋数的程序
  5. Oracle学习:子查询 (sql 嵌套 sql)
  6. Center os vi
  7. 如何使用代码获得ABAP repository object不同版本的内容
  8. Android KeyStore流程
  9. Symfony 4.2.4 和 3.4.23 发布,经典 PHP Web 开发框架
  10. 抖音公布“美好音符年”活动数据:这个省的用户最好运
  11. Kinect 开发 —— 面部追踪
  12. Xml中SelectSingleNode方法,xpath查找某节点用法
  13. 2018最新电大网考计算机,2018年最新电大网考计算机应用基础统考试题.doc
  14. 二、VS插件之VassistX
  15. Linux SPI驱动(Linux驱动开发篇)
  16. 聊聊旷厂黑科技 | 更真切感受影像世界的美好,旷视实时双超AI算法还原你的“夏日回忆”...
  17. 【OpenGL】笔记二十一、Alpha测试、混合测试
  18. 游戏阴阳师启动闪退问题解决
  19. Oracle12C--触发器(52)
  20. JS如何调用Android原生方法

热门文章

  1. Java实现心跳机制
  2. springmvc 注解总结
  3. Effective Java之对可恢复的情况使用受检异常,对编程错误使用运行时异常(五十八)
  4. ActiveMQ实战篇之 java和spring xml创建Broker(一)
  5. hadoop2.6.0安装详细步骤
  6. Leecode 21. 合并两个有序链表——Leecode大厂热题100道系列
  7. KeyError: 报错_python系列学习笔记
  8. Web前端开发笔记——第三章 CSS语言 第四节 CSS列表、表格样式
  9. MongoDB数据库(了解MongoDB及基础命令,备份数据库)
  10. 多维数组的索引与切片_Numpy库使用入门(二)数据的索引和切片