Redis 实战 —— 14. Redis 的 Lua 脚本编程

简介

Redis 从 2.6 版本开始引入使用 Lua 编程语言进行的服务器端脚本编程功能,这个功能可以让用户直接在 Redis 内部执行各种操作,从而达到简化代码并提高性能的作用。 P248

在不编写 C 代码的情况下添加新功能 P248

通过使用 Lua 对 Redis 进行脚本编程,我们可以避免一些减慢开发速度或者导致性能下降对常见陷阱。 P248

将 Lua 脚本载入 Redis P249
  • SCRIPT LOAD 命令可以将脚本载入 Redis ,这个命令接受一个字符串格式的 Lua 脚本为参数,它会把脚本存储起来等待之后使用,然后返回被存储脚本的 SHA1 校验和
  • EVALSHA 命令可以调用之前存储的脚本,这个命令接收脚本的 SHA1 校验和以及脚本所需的全部参数
  • EVAL 命令可以直接执行指定的脚本,这个命令接收脚本字符串以及脚本所需的全部参数。这个命令除了会执行脚本之外,还会将被执行的脚本缓存到 Redis 服务器里面

由于 Lua 的数据传入和传出限制, Lua 与 Redis 需要进行相互转换。因为脚本在返回各种不同类型的数据时可能会产生含糊不清的结果,所以我们应该尽量显式的返回字符串。 P250

我们可以在 官方文档 中找到 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
  • Lua 脚本跟单个 Redis 命令以及 MULTI/EXEC 事务一样,都是原子操作
  • 已经对结构进行了修改的 Lua 脚本将无法被中断
    • 不执行任何写命令对只读脚本:可以在脚本对运行时间超过 lua-time-limit 选项指定的时间之后,执行 SCRIPT KILL 命令杀死正在运行对脚本
    • 有写命令的脚本:杀死脚本将导致 Redis 存储的数据进入一种不一致的状态。在这种情况下

使用 Lua 重写锁和信号量 P254

如果我们事先不知道哪些键会被读取和写入,那么就应该使用 WATCH/MULTI/EXEC 事务或者锁,而不是 Lua 脚本。因此,在脚本里面对未被记录到 KEYS 参数中的键进行读取或者写入,可能会在程序迁移至 Redis 集群的时候出现不兼容或者故障。 P254

获取锁在目前已不需要使用 Lua 脚本实现,可以直接使用 SET ,并用 PXNX 选项即可在键不存在的时候设置带过期时间的值。释放锁时为了保证释放的时自己获取的锁,需要使用 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

组成分片列表的每个分片都会被命名为 <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 脚本中,原子性保证了一定会弹出分片列表两端的元素。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容