redis执行lua脚本

0 篇文章 0 订阅

转载自琦彦的博客

EVAL 脚本 numkeys 键[键…] arg [arg …]

自Redis2.6.0版本起可用。
时间复杂度:取决于执行的脚本。

EVAL介绍
EVAL和EVALSHA用于从Redis2.6.0版本,开始使用内置在Redis中的Lua解释器来评估脚本。
EVAL的第一个参数是一个Lua 5.1脚本。脚本不需要定义一个Lua函数(不应该)。这只是一个将在Redis服务器上下文中运行的Lua程序。

EVAL的第二个参数是脚本后面的参数个数,(从第三个参数开始)代表Redis键名称。参数可以通过Lua中使用来访问KEYS全局变量在基于一个数组(这样的形式KEYS[1],KEYS[2]...)

所有其他参数不应该代表键名称,可以通过ARGV全局变量被Lua使用,非常类似于键做了什么(如: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"

注意:正如你所看到的,Lua数组是以Redis多个批量回复的形式返回的,这是一个Redis返回类型,你的客户端类库可能会用你的编程语言转换成一个Array类型。

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

redis.call()
redis.pcall()

redis.call()类似于redis.pcall(),唯一的区别是,如果一个Redis命令调用会导致一个错误,redis.call()将引发一个Lua错误,反过来会强制EVAL向命令调用者返回一个错误,然而redis.pcall将捕捉错误,并返回一个Lua table代表错误。redis.call()和redis.pcall()函数的参数,是Redis命令格式的所有参数:

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

上面的脚本将键foo设置为字符串bar。然而,它违反了EVAL命令的语义,因为脚本使用的所有键都应该使用KEYS数组传递

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

在执行之前,必须先分析所有Redis命令,以确定命令将在哪些键上运行。为了使EVAL成为真,键必须明确地传递。这在许多方面都很有用,但特别是要确保Redis群集可以将请求转发到适当的群集节点。
请注意:为了向用户提供使用Redis单实例配置的机会,不会执行此规则,但要以编写与Redis群集不兼容的脚本为代价。
Lua脚本可以使用一组转换规则,返回从Lua类型转换为Redis协议的值。

Lua和Redis数据类型之间的转换

当Lua使用call()or 调用Redis命令时,Redis返回值被转换为Lua数据类型pcall()。同样,在调用Redis命令和Lua脚本返回值时,Lua数据类型转换为Redis协议,以便脚本可以控制EVAL返回给客户端的值。
数据类型之间的这种转换的设计方式是,如果将Redis类型转换为Lua类型,然后将结果转换回Redis类型,则结果与初始值相同。
换句话说,Lua和Redis类型之间存在一对一的转换。下表显示了所有的转换规则:
Redis到Lua转换表。

Redis integer reply -> Lua number
Redis bulk reply -> Lua string
Redis multi bulk reply -> Lua table (may have other Redis data types nested)
Redis status reply -> Lua table with a single ok field containing the status
Redis error reply -> Lua table with a single err field containing the error
Redis Nil bulk reply and Nil multi bulk reply -> Lua false boolean type

Lua到Redis转换表。

Lua number -> Redis integer reply (the number is converted into an integer)
Lua string -> Redis bulk reply
Lua table (array) -> Redis multi bulk reply (truncated to the first nil inside the Lua array if any)
Lua table with a single ok field -> Redis status reply
Lua table with a single err field -> Redis error reply
Lua boolean false -> Redis Nil bulk reply.

还有一个额外的Lua-to-Redis转换规则,它没有对应的Redis到Lua转换规则:

Lua boolean true -> Redis integer reply with value of 1.

还有两个重要的规则要注意:

  • Lua有一个数字类型,Lua numbers。整数和浮点数没有区别。所以我们总是把Lua数字转换成整数回复,如果有小数部分的话去掉数字的小数部分。如果你想从Lua返回一个浮点数,你应该像字符串一样返回它,就像Redis本身一样(例如见ZSCORE命令)。没有简单的方法,
  • 在Lua数组中有nils(There is no simple way to have nils inside Lua arrays),这是的Lua表语义的结果,所以当Redis的一个Lua数组转换成Redis的协议,如果遇到nil的转换停止。

以下是几个转换示例:

> eval "return 10" 0
(integer) 10
> eval "return {1,2,{3,'Hello World!'}}" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 3
2) "Hello World!"
 

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

最后一个例子展示它是如何从Lua获得,确切redis.call()或者redis.pcall()的返回值 ,如果命令直接调用将被返回。
在下面的例子中,我们可以看到如何处理带有nil的浮点数组和数组:

> eval "return {1,2,3.3333,'foo',nil,'bar'}" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"

正如你可以看到3.333转化成3,bar是永远不会返回的字符串,因为前面是零。

脚本的原子性
Redis使用相同的Lua解释器来运行所有的命令。另外,Redis保证以原子方式执行脚本:执行脚本时不会执行其他脚本或Redis命令。这个语义类似于MULTI / EXEC。从所有其他客户的角度来看,脚本的效果要么不可见,要么已经完成。
但是这也意味着执行缓慢的脚本不是一个好主意。创建快速脚本并不难,因为脚本开销非常低,但是如果要使用慢速脚本,则应该意识到在脚本运行时,没有其他客户端可以执行命令。

错误处理
如前所述,redis.call()导致Redis命令错误的调用会停止脚本的执行并返回一个错误,使得脚本错误显而易见:

> del foo
(integer) 1
> lpush foo a
(integer) 1
> eval "return redis.call('get','foo')" 0
(error) ERR Error running script (call to f_6b1bf486c81ceb7edf3c093f4c48582e38c0e791): ERR Operation against a key holding the wrong kind of value

使用redis.pcall()不会引发错误,但错误对象是在上述规定的格式返回(作为一个 Lua table 与err字段)。该脚本可以通过返回redis.pcall()的错误对象,将确切的错误传递给用户。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值