蓝天

Redis之eval+lua实现初步

目录

目录 1

1. 前言 1

2. 执行方式 1

3. 执行过程 3

4. 使用原则 3

 

1. 前言

Redis的实现保证eval的执行是原子的,即使eval执行的lua超时,Redis也不会自动终止执行。

官方说明如下:

When a script reaches the timeout it is not automatically terminated by Redis since this violates the contract Redis has with the scripting engine to ensure that scripts are atomic.

2. 执行方式

截止到5.0.5版本,Redis都是将lua解释为一MULTI+EXEC方式执行。当执行以下Redis语句时:

eval "redis.call('set',KEYS[1],ARGV[1]);redis.call('set',KEYS[2],ARGV[2])" 2 k1 k2 12 ab

 

观察它的AOF文件,可以发现内幕:

*1 // 1表示参数个数,包括命令令本身,也就是说命令不带参数

$5 // 第一个参数的字节数,MULTI命令为五个字节

MULTI // Redis命令字

*3 // 表示共三个参数,包括命令字本身

$3

set // Redis命令字

$2 // 为“k1”的字节数

k1 // Redis Key

$2 // 为“12”的字节数

12 // Redis Value

*3

$3

set // Redis命令字

$2

k2

$2

ab

*1

$4

EXEC

 

如果lua脚本有语法错误,可能导致部分执行,如执行下述脚本:

eval "redis.call('set',KEYS[1],ARGV[1]);redis.call('incrby',KEYS[2],ARGV[2])" 2 k1 k2 ab 12

 

将得到错误:

ERR Error running script (call to f_de45a142dc8b96e1634403ac2a3f4ccfef4d9dc9): @user_script:1: ERR value is not an integer or out of range

 

原因是前一段脚本已将k2的值设置为ab,因此为非整数值,不能对该Keyincrby操作。这个时候AOF将只记录已成功执行部分:

*1

$5

MULTI

*3

$3

set

$2

k1

$2

ab

*1

$4

EXEC

 

所以在使用lua之前,一定要先测试好,排除掉语法错误。不然因Redis不支持回滚,会出现中间状态。如果确实需要回滚,也应当在同一段lua中完成提交或回滚。

Redis缓存每一段执行的lua脚本,并且不会主动释放,除非外部调用Redis命令“SCRIPT FLUSH”。Redis对执行的lua脚本做SHA,并用SHA值唯一标识该段lua脚本,后续可直接调用evalsha来执行该脚本,从而避免每次调用传入大段lua脚本。

另外,也可以执行Redis命令“SCRIPT KILL”来主动终止正在执行的lua脚本。但能终止的lua脚本仅限还未执行过写(write)操作,如果被KILLlua脚本已执行了任意写操作,则“SCRIPT KILL”不能终止它的执行,这样约束的原因是为保证eval命令的原子性,不出现中间结果。

如果仍然要终止已执行写操作的lua脚本,只能通过执行“SHUTDOWN NOSAVE”方式,以阻止中间结果(half-written)的存在。

对于复杂的lua脚本 ,可执行redis-cli并指定参数“--ldb”,即可简单快捷的调试lua脚本。

3. 执行过程

Lua的执行过程如下:

-> 调用lua脚本解释器执行lua脚本

-> lua脚本解释器将lua脚本翻译成redis命令

-> redis状态机中应用lua脚本所执行到的redis命令

-> redis以MULTI+EXEC方式将所执行的redis命令写入到AOF文件

-> 响应客户端执行结果

4. 使用原则

使用eval+lua最好遵守以下原则:

1) 执行时间尽可能短,一定要小于连接的超时时长,可使用命令slowlog get”观察实际情况;

2) 在应用到生产环境之前,测试保证没有语法错误;

3) 不要硬编码KeyValue到脚本中,不然缓存lua脚本的内存越积越多。

 

posted on 2019-09-03 19:51  #蓝天  阅读(3638)  评论(0编辑  收藏  举报

导航