介绍
Lua脚本
背景
Redis是一种抽象数据类型的特定领域语言,由各种命令组成。大多数命令专门用于操作不通的数据类型。每次发送命令均需要执行至此网络请求。所以Redis提供了一个编程接口,支持服务器执行用户自定义的任意脚本。有助于减少网络流量,并提高整体性能。在脚本中可以执行多个命令,封装一定的业务逻辑。
运行脚本
首先,自Redis2.6.0以来,EVAL命令支持运行服务器端脚本。Eval脚本提供了一种快速而直接的方法,让Redis临时运行脚本。这意味着脚本必须在应用程序中维护,以便于随时加载。随着应用程序的发展,这种方法后续更难开发和维护。
其次,在v7.0中添加的Redis函数本质上是作为一类数据库元素的脚本。因此,函数将脚本与应用程序逻辑分离,并支持脚本的独立开发、测试和部署。若要使用函数,需要先加载它们,然后才能供所有连接的客户端使用。在这种情况下,将函数加载到数据库将成为一项管理部署任务(例如加载Redis模块),这会将脚本与应用程序分开。
在运行脚本或函数时,Redis保证其原子执行。脚本的执行会在整个时间内阻止所有服务器活动,类似于事务的语义。这些语义意味着脚本的所有效果要么尚未发生,要么已经发生。已执行脚本的阻塞语义始终适用于所有连接的客户端。
所以必须要注意的是执行的脚本不是是一个慢脚本。如果执行过慢,会阻塞所有的客户端请求。
用法
使用 EVAL 命令开始使用 Redis 编写脚本。
bash
> EVAL "return 'Hello, scripting!'" 0
"Hello, scripting!"
在此示例中,EVAL采用两个参数。第一个参数是一个字符串,由脚本的Lua源代码组成。该脚本不需要包含Lua函数的任何定义。它只是一个将在Redis引擎上下文中运行的Lua程序。
第二个参数是脚本正文后面的参数数,从第三个参数开始,表示Redis键名。在此示例中,我们使用值0,因为我们没有为脚本提供任何参数,无论是否为键的名称。
执行上下文可以通过KEYS和ARGV全局运行时变量使参数可供脚本使用。KEYS表预先填充了在执行脚本之前提供给脚本的所有键名参数,而ARGV表具有类似的用途,但用于常规参数。
bash
redis> EVAL "return { KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3] }" 2 key1 key2 arg1 arg2 arg3
1) "key1"
2) "key2"
3) "arg1"
4) "arg2"
5) "arg3"
Redis事务
原理
Redis事务允许执行一组命令在一个步骤中,它们以MULTI
、EXEC
、DISCARD
和WATCH命令为中心。Redis事务提供两项重要保证:
-
事务中的所有命令都序列化并执行顺序。另一个客户端发送的请求永远不会在执行Redis事务的过程中提供服务。这保证了命令作为单个命令执行隔离操作。
-
EXEC
命令触发事务中所有命令的执行,因此如果客户端在事务,在调用EXEC
命令之前,不执行任何操作,如果调用EXEC
命令,则所有执行操作。使用仅追加文件时,Redis确保使用单个write(2) syscall将事务写入磁盘。但是,如果Redis服务器崩溃或被系统管理员杀死在某种程度上,可能只有部分操作已注册。Redis将在重新启动时检测到此情况,并退出并显示错误。使用redis-check-aof该工具可以修复仅附加将删除部分事务的文件,以便服务器可以重新启动。
从版本2.2开始,Redis以乐观锁定的形式,以一种非常相似的方式检查和设置(CAS)操作。
用法
使用MULTI命令输入Redis事务。命令总是回复OK。此时,用户可以发出多个命令。Redis不会执行这些命令,而是会排队他们。调用EXEC后,将执行所有命令。
相反,调用DISCARD将刷新事务队列并退出交易。
bash
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
从上面的会话中可以清楚地看出,EXEC返回一个回复数组,其中每个元素都是单个命令的回复在事务中,命令的发出顺序相同。
事务中命令错误
从Redis2.6.5开始,服务器会在命令累积过程中检测到错误。然后,它将拒绝执行事务,在EXEC期间返回错误,从而丢弃事务。
相反,EXEC之后发生的错误不会以特殊方式处理:即使某些命令在事务期间失败,所有其他命令也会被执行。
回滚?
Redis事务不支持回滚。
Redis Lua脚本和事务使用场景
对于redis中的事务类操作,还需要考虑其他事项,即redis脚本,它们是事务性的。可以用 Redis 事务 做的所有事情,也可以用脚本来做,通常脚本会更简单、更快捷。
缺点
Lua脚本缺点
- 所有客户端必须要维护所有的脚本副本。
- EVAL是一种反模式。它们往往在短期内看起来有效,但长期来看会增加技术债务、降低代码质量和团队效率。
- 脚本之间无法互相调用;
- 无法直接支持redis cluster模式,需要运算key对应的哈希槽。
Redis事务缺点
- Redis事务不支持归滚;
- Redis事务每条命令都与redis服务器进行一次网络交互;