报名参加CloudWeGo黑客松,奖金直推双丰收! 了解详情
写点什么

Redis 进阶应用:Redis+Lua 脚本实现复合操作

  • 2020-02-07
  • 本文字数:2370 字

    阅读完需:约 8 分钟

Redis进阶应用:Redis+Lua脚本实现复合操作

一、引言

Redis 是高性能的 key-value 数据库,在很大程度克服了 memcached 这类 key/value 存储的不足,在部分场景下,是对关系数据库的良好补充。得益于超高性能和丰富的数据结构,Redis 已成为当前架构设计中的首选 key-value 存储系统。


虽然 Redis 官网上提供了 200 多个命令,但做程序设计时还是避免不了为了实现一小步业务逻辑而多次调用 Redis 的情况。


以 compare and set 场景为例。如果使用 Redis 原生命令,需要从 Redis 中获取这个 key,然后提取其中的值进行比对:如果相等就不做处理;如果不相等或者 key 不存在则将 key 设置成目标值。仅仅一个单点的 compare and set 操作就需要与 Redis 通讯两次。


此外,这种分散操作无法利用 Redis 的原子特性,占用多次网络 IO。


今天我们就来探讨一下如何优雅地应对上述场景。

二、Redis 与 Lua

在介绍 Lua 之前,我们需要先对这个语言有个初步了解。Lua 是一个小巧的脚本语言,几乎可以运行在所有操作系统和平台上。我们一般不会用 Lua 处理特别复杂的事务,因此只需了解一些 lua 的基本语法即可。


Redis 问世之后,其开发者也意识到了开篇提到的问题,因此 Redis 从 2.6 版本开始支持 Lua 脚本。新版本的 Redis 还支持 Lua Script debug,感兴趣的小伙伴可以去官网的 Documentation 中找到对应介绍和 QuickStart。


有了 Lua 脚本之后,使用 Redis 程序时便能够在以下方面实现显著提升:


  • 减少网络开销:本来 N 次网络请求的操作,可以用一个请求完成。原先 N 次请求的逻辑放在 Redis 服务器上完成,减少了网络往返时延;

  • 原子操作:Redis 会将整个脚本作为一个整体执行,中间不会被其他命令插入。这是一个重要特性,一定要拿小本本记好。至于为什么是一个原子操作,我们以后再分析;

  • 复用:客户端发送的脚本会永久存储在 Redis 中。这样其他客户端就可以复用这一脚本,而不需要使用代码完成同样的逻辑。


所以现在流传一句话:要想学好 Redis,必会 Lua Script。

三、通过 Lua 脚本实现 compare and set

接下来我们就实现一个简单的 compare and set,并通过这个例子感受一下 Lua 脚本给 Redis 使用带来的全新体验。


首先看一下如何让 Redis 执行 Lua 脚本。

3.1 Redis 的 EVAL

Redis 127.0.0.1:6379> EVAL script  numkeys key [key ...] arg [arg ...]
复制代码


  • script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。

  • numkeys: 用于指定键名参数的个数。

  • key [key …]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的 Redis 键(key)。在 Lua 中,这些键名参数可以通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] ,KEYS[2],依次类推)。

  • arg [arg …]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。


这里借用一下官网的例子。


1565063153728030309.jpeg


上述脚本直接返回了入参。


  • eval 为 Redis 关键字;

  • 第一个引号中的内容就是 Lua 脚本;

  • 2 为参数个数;

  • key1 和 key2 是 KEYS[1]、KEYS[2]的入参;

  • first 和 second 是 ARGV[1],ARGV[2]的入参。


大家可以简单地将 KEYS[1],KEYS[2], ARGV[1],ARGV[2]理解为占位符。

3.2 执行脚本文件和缓存脚本

如果只能在命令行中写脚本执行,遇到复杂的脚本程序岂不是会抓狂?


下面我们来看一下,如何让 Redis 执行 Lua 脚本文件,同时也验证一下 lua 脚本的复用特性(以后我们再也不需要定期批量删除某些符合特定规则的 key 了)。


Redis 127.0.0.1:6379> SCRIPT LOAD  scriptRedis 127.0.0.1:6379> EVALSHA sha1  numkeys key [key ...] arg [arg ...]
复制代码


Redis 提供了一个 SCRIPTLOAD 命令,命令后面的 script 即为 Lua 脚本。命令将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。执行命令后,Redis 会返回一个 SHA1 串,第二个 EVALSHA 命令即可执行。


需要注意的是,脚本可以在缓存中保留无限长的时间,直到执行完 SCRIPT FLUSH。我们来看一下效果。


1565063162744000455.jpeg


Redis 还支持直接执行 Lua 脚本文件。首先编写并存储一个 Lua 脚本。


1565063169363042305.jpeg


然后调用 Redis-cli –eval 命令


1565063176794018397.jpeg


Redis-cli –eval 命令语法基本与原 eval 语法相同。

3.3 使用 Lua 脚本实现 compare and set

compareand set 的实现逻辑是这样的:首先获取 Redis 中指定 key 的 value,然后与给定值进行比较:如果相等,则将 key 设定为目标值并返回一个标识符;如果不相等,则不作任何操作并返回一个标识符。


if Redis.call('get', KEYS[1]) == ARGV[1]  then     Redis.call('set', KEYS[1], ARGV[2]);     return 1else     return 0 end
复制代码


下面我们来测试一下这个脚本。


首先向 Redis 的指定 key compareAndSet:key 写入一个值 value


1565063186284056507.jpeg


在 Redis 中执行 lua 脚本


1565063195364039755.jpeg


可以看到第一次执行返回 1,说明修改成功了;再使用原参数执行时返回 0,说明没有做任何修改。我们再查询一下 compareAndSet:key 这个 key


1565063202334030640.jpeg


可以看到 compareAndSet:key 这个 key 已经被修改为 new_value 了。

四、总结

我们通过 lua 脚本实现了一个简单的 compareAndSet 操作。


下面我们通过这个例子来验证一下开篇提到的特性。


  • 减少网络开销:不使用脚本的情况下,我们实现一个 compareAndSet 至少需要与 Redis 交互两次,而现在只需要执行一次操作即可完成;

  • 原子操作:得益于 Redis 的设计,Redis 会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心出现竞态条件,无需使用事务,感兴趣的可以百度或等待以后后续文章更新;

  • 复用:可以将一系列操作封装成一个 Lua 脚本,存储在文件或 Redis 上,下次使用时直接调用即可。


读到这里,希望你已经对 Redis+Lua 有了一定的了解,并能使用脚本完成一些简单的复合操作。后续还会继续更新一些基于 Lua 脚本+java 程序实现的分布式数据结构,如延迟队列、可重入锁等,感兴趣的小伙伴可以持续关注。


本文转载自宜信技术学院。


原文链接:http://college.creditease.cn/detail/284


2020-02-07 20:381723

评论 1 条评论

发布
用户头像
一直在纠结这个原子性,看到这里明白了,我一直纠结的是回滚,因为redis. call一旦执行就不会被撤销,所以比如编程错误,例如语法类型错误,lua script脚本还是会继续执行脚本里的其他的命令
2021-01-31 23:44
回复
没有更多了
发现更多内容

Redis崩吗?来一起搞定 Redis 实践中的常见问题!

Steven

redis

关于FTP文件传输协议说明,带你了解更详细的文件传输协议

镭速

行业盛会丨九科信息亮相第十一届中国电子信息博览会(CITE2023),与您共享科技盛宴

九科Ninetech

关于 IDP 的五大认知误解

SEAL安全

IdP 平台工程

详解 Flink Catalog 在 ChunJun 中的实践之路

袋鼠云数栈

flink

面试还不懂Netty,看这篇文章就够了!

程序员小毕

Java 程序员 后端 Netty 架构师

NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型压缩算法等

汀丶人工智能

人工智能 自然语言处理 深度学习 关系抽取 命名实体识别

漫谈 ChatGPT 与问答式 BI

观远数据

数据分析 BI ChatGPT

OpenSea交易平台开发NFT系统部署技术

薇電13242772558

NFT

【福利】ChatGPT免费体验期延长,商用版正式开启预约!

BeeWorks

现在学C4D还是Blender好?这俩有啥区别?

Finovy Cloud

blender C4D

PCB拼版对SMT组装的影响,华秋一文告诉你

华秋电子

面试官:谈谈分布式一致性机制

Java永远的神

程序员 分布式 微服务 后端 架构师

智慧公共厕所,重构城市公共服务的新模式@光明源智慧公厕

光明源智慧厕所

智慧城市

精选2023年大厂高频Java面试真题集锦(含答案),面试一路开挂

程序知音

java面试 java架构 Java进阶 后端技术 Java面试八股文

选择低代码,帮企业数字化转型提交一份满分答卷

加入高科技仿生人

软件开发 低代码 数字化 数字转型

干货分享|金融机构如何通过标签画像实现精细化客户运营?

索信达控股

EMQ&阿里云Lindorm联合方案:解决物联网关键业务场景数据处理难题

EMQ映云科技

阿里云 物联网 IoT 数据处理 企业号 4 月 PK 榜

MobTech ShareSDK|分享报错怎么办

MobTech袤博科技

GOPS 全球运维大会来了,龙蜥社区邀您一起了解“系统运维”

OpenAnolis小助手

开源 操作系统 内核 龙蜥社区 GOPS全球运维大会

苹果电脑软件应用打开出现意外退出、崩溃问题解决办法

互联网搬砖工作者

华秋PCB生产工艺分享 | 第十一道之成型

华秋电子

APP频繁改版惹人烦?火山引擎VeDI来帮忙

字节跳动数据平台

数字化 企业数字化 企业号 4 月 PK 榜 APP改版

5 大手段,打造单一可信源代码托管平台|极狐GitLab DevSecOps 助力 SLSA 落地之源代码篇

极狐GitLab

DevOps DevSecOps 源代码 安全审计 SLSA

阿里内部都在疯传!企业级Spring Boot 项目开发实战教程,我先肝了

程序知音

Java 微服务 springboot java架构 Java进阶

携多款产品亮相“深圳先进制造业集群展”,华秋积极探索发展机遇

华秋电子

EasyMR 安全架构揭秘:如何管理 Hadoop 数据安全

袋鼠云数栈

大数据

新旧版本功能对比 | v1.5.0 全新升级

BinTools图尔兹

数据库 社区版 版本更新

Redis进阶应用:Redis+Lua脚本实现复合操作_行业深度_李崇_InfoQ精选文章