一、引言

               redis学了一段时间了,基本的东西都没问题了。从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当中,来扩展其功能。lua脚本是用C语言写的,体积很小,运行速度很快,并且每次的执行都是作为一个原子事务来执行的,我们可以在其中做很多的事情。由于篇幅很多,一次无法概述全部,这个系列可能要通过多篇文章的形式来写,好了,今天我们进入正题吧。

二、lua简介
    
               Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。

              Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。 Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。一个完整的Lua解释器不过200k,在目前所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择。


三、EVAL命令的详解

             1、EVAL简介(Introduction to EVAL)

                      Redis从其2.6.0版本或者更高的版本以后,可以使用 Lua脚本解释器的 EVAL命令和 EVALSHA 命令测试评估脚本。

                      EVAL命令的第一个参数是一个 Lua5.版本1的脚本。脚本不需要定义一个Lua函数(不应该)。它仅仅是一个在Redis服务器的上下文中运行的Lua程序。

                      EVAL命令的第二个参数紧跟Lua脚本后面的那个参数,这个参数表示KEYS参数的个数,从第三个参数开始代表Redis键名称。Lua脚本可以访问由KEYS全局变量(如KEYS [1],KEYS [2],...)组成的一维数据的参数。

                      EVAL最后附加的参数表示的是对应KEYS键名所对应的值,并且Lua脚本可以通过使用ARGV全局变量的访问其值,和KEYS数组的情况差不多(所以ARGV[1],ARGV[2],...)。

以下示例应该阐明上述内容:

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second"

                
注意:正如你所看到的,作为Redis批量回复的形式返回了Lua数组,这是Redis返回的一种类型,在我们实现的客户端库(针对某种语言实现的Redis操作库)中应该会将其转换为针对该编程语言中的特定Array类型。

可以使用两个不同的Lua函数从Lua脚本调用Redis命令:
 

redis.call()
redis.pcall()


                      redis.call()与redis.pcall()非常类似,唯一的区别是,如果Redis命令调用发生了错误,redis.call() 将抛出一个Lua类型的错误,再强制EVAL命令把错误返回给命令的调用者,而redis.pcall()将捕获错误并返回表示错误的Lua表类型。

                      redis.call()和redis.pcall()函数的参数是Redis命令和命令所需要的参数:
 


> eval "return redis.call('set','foo','bar')" 0
                       OK


                     上面的脚本的意思是:将键foo的值设置为字符串的bar,和(set foo bar)命令意义相同。但是它违反了EVAL命令的语义,因为Lua脚本使用的所有键应该通过使用KEYS数组来传递进来:
 


> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
                      OK


                     在执行之前,必须分析所有的Redis命令,以确定命令将在哪些键上运行。为了使EVAL命令执行成功,必须明确传递所需的键。这在很多方面都很有用,但特别要确保Redis群集可以将您的请求转发到适当的群集节点。
                     (All Redis commands must be analyzed before execution to determine which keys the command will operate on. In order for this to be true for EVAL, keys must be passed explicitly. This is useful in many ways, but especially to make sure Redis Cluster can forward your request to the appropriate cluster node.)

                     请注意,此规则未实施,为用户提供滥用Redis单实例配置的机会,这是以编写与Redis集群不兼容的脚本为代价的。
                     (Note this rule is not enforced in order to provide the user with opportunities to abuse the Redis single instance configuration, at the cost of writing scripts not compatible with Redis Cluster.)

                     Lua脚本可以使用一组转换规则返回从Lua类型转换为Redis协议的值。
                     (Lua scripts can return a value that is converted from the Lua type to the Redis protocol using a set of conversion rules.)