最近基于 Kong 做一些东西,其中一个插件的算法设计需要去 redis 做多次读取判断,于是准备把这部分实现逻辑放到 redis 侧,省去多次访问 redis 的网络开销。

redis 的一般用法是对其基础数据结构的读写,因为数据都放在内存,读写非常迅速,进一步还支持执行 lua 脚本,redis 提供了一个 lua 的执行环境(注意是 5.1),并提供了一些在 lua 脚本中调用 redis 命令的方法(redis.call、redis.pcall、redis.log、…),可以通过 script load 或 eval 命令来载入 lua 脚本,载入后得到一个 sha1 值,客户端就可以通过 evalsha <sha1> 来调用这个脚本

evalsha <sha1> <key_num> [<key1> <key2> ...] <arg1> <arg2> ...

从网友的共识来看,最权威的介绍 lua 的书籍是 《Programming in Lua 34rd》,写的过程中对照着看就行。

在脚本放到 redis 后发现有 3 个坑。

第一个坑就是 redis 所支持的 lua 版本过低的问题,还记得一开始我提到,redis 内置的 lua 执行环境版本是5.1,而 5.1 和 5.2+ 的其中一个区别是后者增加了对 goto 的语法支持(虽然 goto 是万恶之源,当年写汇编作业的时候也是 goto 到上头)。而我写的时候先用 java 实现了一遍(顺手用了 continue),再翻译为 lua 代码,因为发现 lua 语法没有 continue,所以按照网上方案用了 goto 来替代(repeat 的方案多套了一层循环,感觉不够直观),不幸的是调试的时候用的 lua5.3 有 goto,这下就有点尴尬。

翻了 so、github,在 2011 年的时候就有网友提了 PR,请社区将 redis 的 lua 版本升级到 5.2,但维护者认为 5.1 就够用了(一个个的,要什么自行车),于是在最新的 redis 6.2.1 里还是 lua5.1。最后调整了分支逻辑,上岸。

第二个坑是 lua 的变量作用范围,放到 redis 以后发现 redis 的 lua 环境禁止操作全局变量,只能改之,传个 table 引用得了。看起来在 lua 里,全局变量是个危险的灰色地带。

第三个坑是 lua 里的数组(换了个马甲其实还是 table)起始下标为 1,改了几个循环体,忍不住停下来想——我是谁?我在哪?

作为一个弱类型的脚本语言,写的时候还是很爽的,特别是对比 java 里返回多值的那种扭捏羞涩。

update:2021.9.1

还有个地方要注意下,使用命令行加载时,不能直接把文件作为参数,得把内容取出来作为输入

redis-cli script load "$(cat xxx.lua)"
分类: CODE

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注