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

Hello, Lua!

我们的第一个Redis Lua 脚本仅仅返回一个字符串,而不会去与redis 以任何有意义的方式交互。
1
local msg = "Hello, world!"
view source
print?

1
local link_id = redis.call("INCR", KEY[1])
2
redis.call("HSET", KEYS[2], link_id, ARGV[1])
3
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表的值:
1
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后的脚本:
view source
print?

1
local link_id = redis.call("INCR", "links:counter")
view source
print?

1
if redis.call("EXISTS",KEYS[1]) == 1 then
2
return redis.call("INCR",KEYS[1])
3
else
4
return nil
5
end
 
脚本加载与注册执行

注意,当Redis在运行Lua脚本的时候,其它的事情什么都干不了!脚本最好只是简单的扩展Redis进行较小的原子操作和简单的逻辑控制需要,Lua脚本中的bug可能引发整个Redis服务器锁—最好保持脚本的简短和易于调试。
虽然这些脚本一般都比较短小,但我们还是希望不要每次执行时都使用完整的Lua脚本,实际上可以在程序一步一步(译注:application boots翻译有难度)开发中注册Lua脚本(或者在你部署时注册),然后用注册后生成的SHA-1标识来进行调用。
view source
print?

1
redis-cli SCRIPT LOAD "return 'hello world'"
 
3
 

脚本加载与注册执行

注意,当Redis在运行Lua脚本的时候,其它的事情什么都干不了!脚本最好只是简单的扩展Redis进行较小的原子操作和简单的逻辑控制需要,Lua脚本中的bug可能引发整个Redis服务器锁—最好保持脚本的简短和易于调试。
虽然这些脚本一般都比较短小,但我们还是希望不要每次执行时都使用完整的Lua脚本,实际上可以在程序一步一步(译注:application boots翻译有难度)开发中注册Lua脚本(或者在你部署时注册),然后用注册后生成的SHA-1标识来进行调用。
view source
print?

1
redis-cli SCRIPT LOAD "return 'hello world'"
 
3
 

何时使用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支持将非常方便:
view source
print?

1
if redis.call("EXISTS", KEYS[1]) == 1 then
2
local payload = redis.call("GET", KEYS[1])
3
return cjson.decode(payload)[ARGV[1]]
4
else
5
return nil
6
end

在这里我们检查看key是否存在,如不存在则快速返回nil。如存在则从Redis中获取JSON值,用cjson.decode()进行解析,然后返回请求内容。
1
redis-cli set apple '{ "color": "red", "type": "fruit" }'
2
=> OK
3
 

加载这段脚本进你的Redis服务器,将JSON数据保存到Redis中,通常是hash。 虽然我们每次访问时都必须解析,但只要你的对象很小,这个操作实际上是非常快的。
如果你的API只是在内部提供,通常需要考虑效率上的问题,MessagePack 是比采用JSON更好的选择,它更小,更快,在Redis(更多场合也是如此),MessagePack是JSON更好的替代品。
view source
print?

1
if redis.call("EXISTS", KEYS[1]) == 1 then
2
local payload = redis.call("GET", KEYS[1])
3
return cmsgpack.unpack(payload)[ARGV[1]]
4
else
5
return nil
6
end

数值转换

Lua和Redis各有自己的一套类型,因此,理解Redis与Lua在边界调用相互转换引起值的改变是非常重要的。一个来自Lua中number返回到Redis客户端时变成了integer—任何数字后面的小数点都被清除了:
view source
print?

1
local indiana_pi = 3.2
2
return indiana_pi

在你运行这段脚本时,Redis将返回一个整数3,丢失了pi中有用的片段。看起来很简单,但是一旦开始进行Redis与中间脚本交互时就需要更小心。例如:
1
local indiana_pi = 3.2
2
redis.call("SET", "pi", indiana_pi)
3
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 ManualLua Tutorial DirectoryEVAL Docsevalsha.com — 偶尔会有垃圾邮件,但内容很好(译注:里面有很多的Lua脚本,以EVALSHA方式提供,超棒,希望对你有用。)

(转) Lua: 给 Redis 用户的入门指导的更多相关文章

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

    转自:http://www.oschina.net/translate/intro-to-lua-for-redis-programmers 可能你已经听说过Redis 中嵌入了脚本语言,但是你还没有 ...

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

    add by zhj : Lua与Multi/EXEC的功能比较像,在执行过程中,redis不会执行其它命令,这就不会有并发访问的问题, 这是非常好的.但Multi/EXEC要求所有命令都是独立的,后 ...

  3. 全网最完整的Redis入门指导

    前言 本文提供全网最完整的Redis入门指导教程,下面我们从下载Redis安装包开始,一步一步的学习使用. 下载Redis 官网提供的Redis安装包是服务于Linux的,而我们需要在Window下使 ...

  4. Redis入门指导

    前言 本文提供全网最完整的Redis入门指导教程,下面我们从下载Redis安装包开始,一步一步的学习使用. 下载Redis 官网提供的Redis安装包是服务于Linux的,而我们需要在Window下使 ...

  5. Lua 与 Redis

    Lua 与 Redis 标签: Java与NoSQL 从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis - 案例-实现访问频率限制: 实现访问者 $ip 在一定的 ...

  6. Redis详解入门篇

    Redis详解入门篇 [本教程目录] 1.redis是什么2.redis的作者3.谁在使用redis4.学会安装redis5.学会启动redis6.使用redis客户端7.redis数据结构 – 简介 ...

  7. Redis详解入门篇(转载)

    Redis详解入门篇(转载) [本教程目录] 1.redis是什么2.redis的作者3.谁在使用redis4.学会安装redis5.学会启动redis6.使用redis客户端7.redis数据结构 ...

  8. 【建议收藏】Redis超详细入门教程大杂烩

    写在前边 Redis入门的整合篇.本篇也算是把2021年redis留下来的坑填上去,重新整合了一翻,点击这里,回顾我的2020与2021~一名大二后台练习生 NoSQL NoSQL(NoSQL = N ...

  9. React-Native入门指导之iOS篇 —— 一、准备工作

    React-Native 入门指导系列教程目录 一.准备工作 (已完成) 二.项目介绍与调试 三.CSS样式与Flex布局 四.常用UI控件的使用 五.JSX在React-Native中的应用 六.事 ...

随机推荐

  1. stuff in /proc/PID/

    Table of Contents 1. /proc/PID/cwd 2. /proc/PID/clear_refs 3. /proc/PID/coredump_filter 4. /proc/PID ...

  2. 不让复制是不可能的----js获取选中文字

    在360百科.知乎上经常会遇见禁止复制文本的情形,这能挡住一部分人复制,却挡不住程序员的复制. HTML都给我了,难道一小段文本我都拿不下来吗? F12打开控制台,然后选中文本,在控制台下粘贴以下代码 ...

  3. 什么是EPEL 及 Centos上安装EPEL

    RHEL以及他的衍生发行版如CentOS为了稳定,官方的rpm repository提供的rpm包为了服务器安全稳定更新往往是很滞后的,很多时候需要自己编译那太辛苦了,而EPEL恰恰可以解决这两方面的 ...

  4. umdh windbg分析内存泄露

    A.利用工具umdh(user-mode dump heap)分析:此处以程序MemoryLeak.exe为例子 1.开启cmd 键入要定位内存泄露的程序gflags.exe /i memroylea ...

  5. 模拟器集成3DTouch-b

    本文主要讲解3DTouch各种场景下的集成,开发主屏幕应用icon上的快捷选项标签(Home Screen Quick Actions),静态设置 UIApplicationShortcutItem ...

  6. Swift 基础知识

    前言 Swift 是一门强类型语言,全面支持 Unicode 编码. Swift 中的定义和实现是在同一个单元中的,通常一个 Swift 源代码单文件是以 .swift 结尾的. Swift 不需要单 ...

  7. mongodb常用命令小结

    一.基于条件的查询 db.getCollection('monitor_log') .find({"requestUrl" : /app\/v1\/findcards\/cn/, ...

  8. golang学习笔记 ---面向并发的内存模型

    Go语言是基于消息并发模型的集大成者,它将基于CSP模型的并发编程内置到了语言中,通过一个go关键字就可以轻易地启动一个Goroutine,与Erlang不同的是Go语言的Goroutine之间是共享 ...

  9. WARNING: The host 'r6' could not be looked up with /usr/local/mysql/bin/resolveip.

    初始化MySQL数据库提示以下信息: # /usr/local/mysql/scripts/mysql_install_db \ > --defaults-file=/etc/my.cnf \ ...

  10. HTML5应用程序缓存实现离线Web网页或应用

    HTML5应用程序缓存和浏览器缓存的区别.(有些)浏览器会主动保存自己的缓存文件以加快网站加载速度.但是要实现浏览器缓存必须要满足一个前提,那就是网络必须要保持连接.如果网络没有连接,即使浏览器启用了 ...