文章目录
-
1.关于Lua
- 2.redis中使用Lua
-
2.1 在shell中尝试使用Lua脚本
* 2.2 传入参数的脚本- 3.Python中使用Lua嵌入redis
1.关于Lua
Lua [1] 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。
简单来说Lua就是一门脚本语言,因为其简洁的语法和小巧的核心,所以被广泛应用在应用程序的嵌入中,比较著名的就是《WOW》《大话西游2》等一些游戏中,用到了这门脚本语言。
Lua的语法相对来说比较简单,有兴趣的同学可以 Lua教程–菜鸟教程
基于Lua诸多的优点,redis中支持Lua脚本的使用。
2.redis中使用Lua
1
2
3
4
5
6
7
8
9
10
11
12
13 1 1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。
2
3 2、原子性的操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
4
5 3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。
6
7 4、速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。
8
9 5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux,同样Mac平台也没问题, 再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript).
10
11 6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。
12
13
其实我自己在使用Redis中内嵌Lua脚本的时候,感觉就是很像SQL型数据库里的存储过程,因为本身Redis是没有存储过程这个概念的,不过根据上面提到的 2.原子性操作 和 3.代码复用 相信你也和我感受一样,这不就是和存储过程一个道理吗。
2.1 在shell中尝试使用Lua脚本
1
2
3
4
5
6
7 1 $ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...
2 --eval,告诉redis-cli读取并运行后面的lua脚本
3 path/to/redis.lua,是lua脚本的位置
4 KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取
5 ARGV[1] ARGV[2],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。
6
7
很容易理解,EVAL命令后参数是script,**这里指的就是Lua脚本,**后面的参数,是脚本中可以取的参数(就像Shell脚本一样)
在 Lua 脚本中,可以使用两个不同函数来执行 Redis 命令,它们分别是:
1
2
3
4
5 1redis.call()
2
3redis.pcall()
4
5
这两个函数的唯一区别在于它们使用不同的方式处理执行命令所产生的错误。
这个例子> eval "return redis.call('set','foo','bar')" 0就是redis执行了set操作,让一个String为‘bar’,之所以有return是为了返回操作的结果,就像我们平时操作redis一样,redis会返回一个结果。
当然也可以直接调用函数,而不返回值,那么看到的是nil
2.2 传入参数的脚本
上面这个例子中,eval "return redis.call('set',KEYS[1],'test')" 1 test_key,参数 1是传入的key的数量,这里我们传入了一个key所以参数是1,而Lua脚本中的KEYS[1] 相当于传入参数的一个数组。
当然传入多个参数,我们使用KEYS[index]就可以取相应下标的参数。
除了传入key(键),也可以传入arg(值),作为参数。
回头再看一眼这个说明,numkeys是key的数量,后面跟了key数组,再后面还有arg数组。也就是说arg数组可以有值也可以不传值。
1
2
3 1eval "return redis.call('set',KEYS[1],ARGV[2])" 1 test_3_key arg3 arg2 arg3
2
3
所以为什么要指定numkeys也很显而易见了,为了区分后面的arg,当然在Lua脚本中,arg也会被作为一个数组,取值的方法也是ARGV[index]。
当然,除了我这里使用到Redis中的String数据类型,其他几种数据类型的操作也可以直接写,就和平时使用shell直接操作redis一样,这里我就不都展示出来了。
3.Python中使用Lua嵌入redis
其实真实使用过程中,Lua脚本要比上面的复杂一些。
比如说下面这个脚本,我们会先判断传入的参数ARGV[1],然后再进行插入到不同队列的操作。
1
2
3
4
5
6
7
8
9
10 1-- 一个简单地判断脚本,内嵌进redis中可以提交执行速度
2if (ARGV[1] == 'ok')
3then
4 return redis.call('lpush',KEYS[1],ARGV[2])
5end
6if (ARGV[1] == 'error') then
7 return redis.call('lpush','ERROR_LIST',ARGV[2])
8end
9
10
而在Python使用API可以说非常的简单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 1#!/usr/bin/env python
2# -*- coding:utf-8 -*-
3import redis
4
5if __name__ == '__main__':
6 r = redis.StrictRedis(host='127.0.0.1', port=6888, db=0)
7 lua_script = """
8 -- 一个简单地判断脚本,内嵌进redis中可以提交执行速度
9 if (ARGV[1] == 'ok')
10 then
11 return redis.call('lpush',KEYS[1],ARGV[2])
12 end
13 if (ARGV[1] == 'error') then
14 return redis.call('lpush','ERROR_LIST',ARGV[2])
15 end
16 """
17 lua_res = r.register_script(lua_script)
18 lua_res(keys=['RIGHT_LIST'],args=['ok','test_arg'])
19 lua_res(keys=['ANY_THING'],args=['error','error_arg'])
20
21
可以看到执行完Python脚本以后的结果。当然在实际使用过程中,可能需要更复杂的逻辑,所以需要本身对于Lua脚本有一定的掌握能力。
- lua-百度百科
- Redis进阶实践之七Redis和Lua初步整合使用
- Redis 命令参考 » Script(脚本) »