Redis 从 2.6 版本开始引入使用 Lua 编程语言进行的服务器端脚本编程功能,这个功能可以让用户直接在 Redis 内部执行各种操作,从而达到简化代码并提高性能的作用。 P248
在不编写 C 代码的情况下添加新功能 P248通过使用 Lua 对 Redis 进行脚本编程,我们可以避免一些减慢开发速度或者导致性能下降对常见陷阱。 P248
将 Lua 脚本载入 Redis P249SCRIPT LOAD 命令可以将脚本载入 Redis ,这个命令接受一个字符串格式的 Lua 脚本为参数,它会把脚本存储起来等待之后使用,然后返回被存储脚本的 SHA1 校验和
EVALSHA 命令可以调用之前存储的脚本,这个命令接收脚本的 SHA1 校验和以及脚本所需的全部参数
EVAL 命令可以直接执行指定的脚本,这个命令接收脚本字符串以及脚本所需的全部参数。这个命令除了会执行脚本之外,还会将被执行的脚本缓存到 Redis 服务器里面
由于 Lua 的数据传入和传出限制, Lua 与 Redis 需要进行相互转换。因为脚本在返回各种不同类型的数据时可能会产生含糊不清的结果,所以我们应该尽量显式的返回字符串。 P250
我们可以在 中找到 Redis 和 Lua 不同类型之间的转换表:
Redis 类型转换至 Lua 类型
Redis Luainteger reply number
bulk reply string
multi bulk reply table (may have other Redis data types nested)
status reply table with a single ok field containing the status
error reply table with a single err field containing the error
Nil bulk reply false boolean type
Nil multi bulk reply false boolean type
Lua 类型转换至 Redis 类型
Lua Redisnumber integer reply (the number is converted into an integer)
string bulk reply
table (array) multi bulk reply (truncated to the first nil inside the Lua array if any)
table with a single ok field status reply
table with a single err field error reply
boolean false Nil bulk reply
boolean true integer reply with value of 1
创建新的状态消息 P251
Lua 脚本跟单个 Redis 命令以及 MULTI/EXEC 事务一样,都是原子操作
已经对结构进行了修改的 Lua 脚本将无法被中断
不执行任何写命令对只读脚本:可以在脚本对运行时间超过 lua-time-limit 选项指定的时间之后,执行 SCRIPT KILL 命令杀死正在运行对脚本
有写命令的脚本:杀死脚本将导致 Redis 存储的数据进入一种不一致的状态。在这种情况下
使用 Lua 重写锁和信号量 P254如果我们事先不知道哪些键会被读取和写入,那么就应该使用 WATCH/MULTI/EXEC 事务或者锁,而不是 Lua 脚本。因此,在脚本里面对未被记录到 KEYS 参数中的键进行读取或者写入,可能会在程序迁移至 Redis 集群的时候出现不兼容或者故障。 P254
获取锁在目前已不需要使用 Lua 脚本实现,可以直接使用 SET ,并用 PX 和 NX 选项即可在键不存在的时候设置带过期时间的值。释放锁时为了保证释放的时自己获取的锁,需要使用 Lua 脚本实现。相关代码已在 实现自动补全、分布式锁和计数信号量 中实现。
移除 WATCH/MULTI/EXEC 事务 P258一般来说,如果只有少数几个客户端尝试对被 WATCH 命令监视对数据进行修改,那么事务通常可以在不发生明显冲突或重试的情况下完成。但是,如果操作需要进行好几次通信往返,或者操作发生冲突的概率较高,又或者网络延迟较大,那么客户端可能需要重试很多次才能完成操作。 P258
使用 Lua 脚本替代事务不仅可以保证客户端尝试的执行都可以成功,还能降低通信开销,大幅提高 TPS 。同时由于 Lua 脚本没有多次通信往返,所以执行速度也会明显快于细粒度锁的版本。
Lua 脚本可以提供巨大的性能优势,并且能在一些情况下大幅地简化代码,但运行在 Redis 内部但 Lua 脚本只能访问位于 Lua 脚本之内或者 Redis 数据库之内的数据,而锁或者 WATCH/MULTI/EXEC 事务并没有这一限制。 P263
使用 Lua 对列表进行分片 P263 分片列表的构成 P263为了能够对分片列表的两端执行推入操作和弹出操作,在构建分片列表时除了需要存储组成列表的各个分片之外,还需要记录列表第一个分片的 ID 以及最后一个分片的 ID 。当分片列表为空时,这两个字符串存储的分片 ID 将是相同的。 P263