Redis 从 2.6 版本开始引入使用 Lua 编程语言进行的服务器端脚本编程功能,这个功能可让用户直接在 Redis 内部执行各类操做,从而达到简化代码并提升性能的做用。 P248
git
P248
经过使用 Lua 对 Redis 进行脚本编程,咱们能够避免一些减慢开发速度或者致使性能降低对常见陷阱。 P248
github
P249
SCRIPT LOAD
命令能够将脚本载入 Redis ,这个命令接受一个字符串格式的 Lua 脚本为参数,它会把脚本存储起来等待以后使用,而后返回被存储脚本的 SHA1 校验和EVALSHA
命令能够调用以前存储的脚本,这个命令接收脚本的 SHA1 校验和以及脚本所需的所有参数EVAL
命令能够直接执行指定的脚本,这个命令接收脚本字符串以及脚本所需的所有参数。这个命令除了会执行脚本以外,还会将被执行的脚本缓存到 Redis 服务器里面因为 Lua 的数据传入和传出限制, Lua 与 Redis 须要进行相互转换。由于脚本在返回各类不一样类型的数据时可能会产生含糊不清的结果,因此咱们应该尽可能显式的返回字符串。 P250
redis
咱们能够在 官方文档 中找到 Redis 和 Lua 不一样类型之间的转换表:数据库
Redis 类型转换至 Lua 类型编程
Redis | Lua |
---|---|
integer 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 | Redis |
---|---|
number | 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
MULTI
/EXEC
事务同样,都是原子操做lua-time-limit
选项指定的时间以后,执行 SCRIPT KILL
命令杀死正在运行对脚本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
P263
P263
为了可以对分片列表的两端执行推入操做和弹出操做,在构建分片列表时除了须要存储组成列表的各个分片以外,还须要记录列表第一个分片的 ID 以及最后一个分片的 ID 。当分片列表为空时,这两个字符串存储的分片 ID 将是相同的。 P263
组成分片列表的每一个分片都会被命名为 <listname>:<shardid>
,并按照顺序进行分配。具体来讲,若是程序老是从左端弹出元素,并从右端推入元素,那么最后一个分配的索引就会逐渐增大,而且新分片的 ID 也会变得愈来愈大。若是程序老是从右端弹出元素,并从左端推入元素,那么第一个分片的索引就会逐渐减小,而且新分片的 ID 也会变得愈来愈小。 P264
当分片列表包含多个列表时,位于分片两端的列表多是被填满的,但位于两端之间的其余列表老是被填满的。 P264
P265
Lua 脚本根据命令 LPUSH
/RPUSH
找到列表的第一个分片或者最后一个分片,而后将元素推入分片对应的列表中,若分片已达个数上限(能够取配置中的 list-max-ziplist-entries
的值 - 1 做为上限),则会自动产生一个新的分片,继续推入,并更新第一个分片或者最后一个分片的分片 ID 。当推入操做执行完毕后,它会返回被推入元素的数量。 P265
P266
Lua 脚本根据命令 LPOP
/RPOP
找到列表的第一个分片或者最后一个分片,而后在分片非空的状况下,从分片里面弹出一个元素,若是列表在执行弹出操做以后再也不包含任何元素,那么程序就对记录着列表两端分片信息的字符串键进行修改(注意只有列表端分片为空时才修改对应的字符串键,而整个列表为空时,不作调整) P267
P267
这一段书上讲得看不懂,也不知道为何须要书中的花式操做才能完成。
我的以为分片列表的阻塞弹出其实并不须要列表自身的阻塞弹出,咱们能够不断执行上述 Lua 脚本实现的弹出元素的操做,若弹出成功,则直接返回,若弹出失败,则睡 1 ms 后继续执行弹出操做,直至弹出成功或者达到超时时间。这样咱们对 Redis 对操做只在 Lua 脚本中,原子性保证了必定会弹出分片列表两端的元素。
本文首发于公众号:满赋诸机(点击查看原文) 开源在 GitHub :reading-notes/redis-in-action