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

本文涉及的产品
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,通用型 2核4GB
简介: 通过阅读本文将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] ,诸如此类)。

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

WechatIMG19_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  script
Redis 127.0.0.1:6379> EVALSHA sha1  numkeys key [key ...] arg [arg ...]

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

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

WechatIMG20_jpeg

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

WechatIMG21_jpeg

然后调用Redis-cli –eval命令

WechatIMG22_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 1
else
     return 0 end

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

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

WechatIMG23_jpeg

在Redis中执行lua脚本

WechatIMG24_jpeg

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

WechatIMG25_jpeg

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

总结

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

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

  • 减少网络开销:不使用脚本的情况下,我们实现一个compareAndSet至少需要与Redis交互两次,而现在只需要执行一次操作即可完成;
  • 原子操作:得益于Redis的设计,Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心出现竞态条件,无需使用事务,感兴趣的可以百度或等待以后后续文章更新;
  • 复用:可以将一系列操作封装成一个Lua脚本,存储在文件或Redis上,下次使用时直接调用即可。

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

作者:李崇

原文首发 UAVStack智能运维

来源:宜信技术学院

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
4天前
|
弹性计算 NoSQL Shell
redis没设置密码,莫名被设置了4个sh脚本
阿里云ECS实例上未设密码的Redis服务被发现含有未知来源的SH脚本,这些脚本定时从外部URL下载并执行代码。这可能是服务器遭受恶意攻击的迹象。建议立即检查系统日志,确认是否被黑,并移除这些脚本。同时,为Redis设置密码,加强安全防护。若不确定,可寻求专业安全团队帮助。
32 2
|
4天前
|
存储 缓存 NoSQL
深入浅出Redis(十):Redis的Lua脚本
深入浅出Redis(十):Redis的Lua脚本
|
4天前
|
存储 NoSQL 调度
Redis Lua脚本:原子性的真相揭秘
【4月更文挑战第20天】
67 0
Redis Lua脚本:原子性的真相揭秘
|
4天前
|
缓存 NoSQL Java
lua脚本在redis中的使用场景
lua脚本在redis中的使用场景
|
4天前
|
NoSQL Java Redis
lua脚本做redis的锁
这段内容是关于使用Redis实现分布式锁的Java代码示例。`RedisLock`类包含`lock`和`unlock`方法,使用`StringRedisTemplate`和Lua脚本进行操作。代码展示了两种加锁方式:一种带有过期时间,另一种不带。还提到了在加锁和解锁过程中的异常处理,并提供了相关参考资料链接。
20 3
|
4天前
|
存储 NoSQL Redis
Redis的Lua脚本有什么作用?
Redis Lua脚本用于减少网络开销、实现原子操作及扩展指令集。它能合并操作降低网络延迟,保证原子性,替代不支持回滚的事务。通过脚本,代码复用率提高,且可自定义指令,如实现分布式锁,增强Redis功能和灵活性。
47 1
|
4天前
|
存储 NoSQL 关系型数据库
使用lua脚本操作redis
使用lua脚本操作redis
51 0
|
4天前
|
NoSQL Java Redis
Redis进阶-lua脚本
Redis进阶-lua脚本
69 0
|
4天前
|
缓存 NoSQL Java
【Redis】5、Redis 的分布式锁、Lua 脚本保证 Redis 命令的原子性
【Redis】5、Redis 的分布式锁、Lua 脚本保证 Redis 命令的原子性
66 0
|
4天前
|
算法 NoSQL Java
springboot整合redis及lua脚本实现接口限流
springboot整合redis及lua脚本实现接口限流
96 0

相关产品

  • 云数据库 Redis 版