失踪人口回来啦!git
有读者问我为何这么久都没有出Redis Lua中学教程,表示村头厕所已经很久没有纸了。其实我早就要写这篇中学教程了,奈何最近太忙了,就一拖再拖,直到今天我终于又开始动笔了。忘记Lua相关概念的同窗能够先回顾一下小学教程。github
中学教程主要分为两部分:Redis Lua的相关命令详解和Lua的语法介绍。redis
前面咱们简单介绍了EVAL和EVALSHA命令。可是只有那点只是是没办法从中学毕业的,所以咱们须要进行更深刻的学习。json
最先可用版本:2.6.0数组
用法:EVAL script numkeys key [key ...] arg [arg ...]缓存
关于用法咱们已经演示过了,其中第一个参数是要执行的Lua脚本,第二个参数是传入脚本的参数个数。后面则是参数的key数组和value数组。bash
在Lua中执行Redis命令的方法咱们也介绍过,就是使用redis.call()和redis.pcall()两个函数。它们之间惟一的不一样就是当Redis命令执行错误时,redis.call()会抛出这个错误,使EVAL命令抛出错误,而redis.pcall()会捕获这个错误,并返回Lua的错误表。服务器
一般咱们约定执行命令的key都须要由参数传入,命令必须在执行以前进行分析,以肯定它做用于哪一个key。这样作的目的是为了在必定程度上保证EVAL执行的Lua脚本的正确性。dom
在Redis执行EVAL命令时,若是脚本中有call()或者pcall()命令,就会涉及到Redis和Lua之间数据类型转换的问题。转换规则要求,一个Redis的返回值转换成Lua数据类型后,再转换成Redis数据类型,其结果必须和初始值相同。因此每种类型是一一对应的。转换规则以下:异步
Redis | Lua |
---|---|
integer | number |
bulk | string |
multi bulk | table |
status | table with a single ok field |
error | table with a single err field |
Nil bulk &Nil multi bulk | false boolean type |
除此以外,Lua到Redis的转换还有一些其余的规则:
来个栗子验证一下:
EVAL "return {1,2,3.3333,'foo',nil,'bar'}" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"
复制代码
能够看到bar没有返回,而且3.333返回了3。
Redis运行全部的Lua命令都使用相同的Lua解释器。当一个脚本正在执行时,其余的脚本或Redis命令都不能执行。这很像Redis的事务multi/exec。这意味着咱们要尽可能避免脚本的执行时间过长。
当脚本进行传播或者写入AOF文件时,Redis一般会将脚本自己进行传播或写入AOF,而不是使用它产生的若干命令。缘由很简单,传播整个脚本要比传播一大堆生成的命令的速度要快。
从Redis3.2开始,能够只复制影响脚本执行结果的语句,而不用复制整个脚本。这个复制整个脚本的方法有如下属性:
在这种模式下,Redis在执行脚本时会收集全部影响数据集的命令,当脚本执行完毕时,命令队列会被放在事务中,发送给AOF文件。
Lua能够经过执行redis.replicate_commands()函数来检查复制模式,若是返回true表示当前是复制命令模式,若是返回false,则是复制整个脚本模式。
脚本复制模式选择好之后,就能够对复制到副本和AOF的方式进行更多的控制。这是一种高级特性,由于滥用会切断主从备份,和AOF持久化。若是咱们只须要在master上执行某些命令时,这一特性就变得颇有用。例如咱们须要计算一些中间值时,只须要在master上计算就好,那么这些命令就没必要进行复制。
从Redis3.2开始,有一个新的命令叫作redis.set_repl(),它能够用来控制复制方式,有以下选项(默认是REPL_ALL):
redis.set_repl(redis.REPL_ALL) -- Replicate to AOF and replicas.
redis.set_repl(redis.REPL_AOF) -- Replicate only to AOF.
redis.set_repl(redis.REPL_REPLICA) -- Replicate only to replicas (Redis >= 5)
redis.set_repl(redis.REPL_SLAVE) -- Used for backward compatibility, the same as REPL_REPLICA.
redis.set_repl(redis.REPL_NONE) -- Don't replicate at all. 复制代码
为了不数据泄露,Redis脚本不容许建立全局变量。若是必须有一个公共变量,可使用Redis的key来代替。在EVAL命令中建立一个全局变量会引发一个异常。
> eval 'a=10' 0
(error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable 'a 复制代码
在Lua脚本中使用SELECT就像在正常客户端中使用同样。值得一提的是,在Redis2.8.12以前,Lua脚本中执行SELECT是会影响到客户端的,而从2.8.12开始,Lua脚本中的SELECT只会在脚本执行过程当中生效。这点在Redis版本升级时须要注意,由于升级先后,命令的语义会改变。
Lua脚本中有许多库,但并非都能在Redis中使用,其中可使用的有:
base
lib.table
lib.string
lib.math
lib.struct
lib.cjson
lib.cmsgpack
lib.bitop
lib.redis.sha1hex
function.redis.breakpoint and redis.debug
function in the context of the Redis Lua debugger.struct, CJSON and cmsgpack是外部库,其余的都是Lua的标准库。
使用redis.log(loglevel,message)函数能够在Lua脚本中打印Redis日志。
loglevel包括:
它们与Redis的日志等级是对应的。
脚本不该该访问外部系统,包括文件系统和其余系统。脚本应该只能操做Redis数据和传入进来的参数。
脚本默认的最大执行时间是5秒(正常脚本执行时间都是毫秒级,因此5秒已经足够长了)。能够经过修改lua-time-limit变量来控制最大执行时间。
当脚本执行时间超过最大执行时间时,并不会被自动终止,由于这违反了脚本的原子性原则。当一个脚本执行时间过长时,Redis会有以下操做:
最先可用版本:2.6.0
用法:EVALSHA sha1 numkeys key [key ...] arg [arg ...]
该命令用来执行缓存在服务器上的脚本,sha1为脚本的惟一标识。
使用EVAL命令必须每次都要把脚本从客户端传到服务器,因为Redis的内部缓存机制,它并不会每次都从新编译脚本,可是传输上仍然浪费带宽。
另外一方面,若是使用特殊命令或者经过redis.conf来定义命令会有如下问题:
为了不这些问题,同时避免浪费带宽,Redis实现了EVALSHA命令。
若是服务器中没有缓存指定的脚本,会返回给客户端脚本不存在的错误信息。
最先可用版本:3.2.0
时间复杂度:O(1)
用法:SCRIPT DEBUG YES|SYNC|NO
该命令用于设置随后执行的EVAL命令的调试模式。Redis包含一个完整的Lua调试器,代号为LDB,可使编写复杂脚本的任务更加简单,在调试模式下,Redis充当远程调试服务器,客户端能够逐步执行脚本,设置断点,检查变量等。想了解更多调试器内容的能够查看官方文档Redis Lua debugger。
LDB能够设置成异步或同步模式。异步模式下,服务器会fork出一个调试会话,不会阻塞主会话,,调试会话结束后,全部数据都会回滚。同步模式则会阻塞会话,并保留调试过程当中数据的改变。
最先可用版本:2.6.0
时间复杂度:O(N),N是脚本数量
返回脚本是否存在于缓存中(存在返回1,不存在返回0)。这个命令适合在管道前执行,以保证管道中的全部脚本都已经加载到服务器端了,若是没有,须要用SCRIPT LOAD命令进行加载。
最先可用版本:2.6.0
时间复杂度:O(N),N是缓存中的脚本数
刷新缓存中的脚本,这一命令常在云服务上被使用。
最先可用版本:2.6.0
时间复杂度:O(1)
中止当前正在执行的Lua脚本,一般用来中止执行时间过长的脚本。中止后,被阻塞的客户端会抛出一个错误。
最先可用版本:2.6.0
时间复杂度:O(N),N是脚本的字节数
该命令用于将脚本加载到服务器端的缓存中,但不会执行。加载后,服务器会一直缓存,由于良好的应用程序不太可能有太多不一样的脚本致使内存不足。每一个脚本都像一个新命令的缓存,因此即便是大型应用程序,也就有几百个,它们占用的内存是微不足道的。
本文介绍了Redis Lua相关的命令。其中EVAL和EVALSHA用来执行脚本。脚本执行具备原子性。脚本的复制和传播能够根据须要设置。脚本中不能定义全局变量。