当前位置: 首页 > 科技观察

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

时间:2023-03-17 01:22:57 科技观察

1.简介Redis是一个高性能的key-value数据库,很大程度上克服了memcached等key/value存储的不足。在某些场景下,它是关系数据库的一个很好的补充。得益于超高性能和丰富的数据结构,Redis成为当前架构设计中首选的key-value存储系统。虽然Redis官网提供了200多条命令,但是在编程时还是免不了为了实现一小步业务逻辑而多次调用Redis。以比较和设置场景为例。如果使用Redis原生命令,需要从Redis中获取key,然后提取value进行比较:如果相等,则不处理;如果不相等或键不存在,则将键设置为目标值。仅仅一个单点的比较和设置操作就需要和Redis通信两次。另外,这种分散操作无法利用Redis的原子性,占用多个网络IO。今天我们就来探讨一下如何优雅的应对上述场景。2.Redis与Lua在介绍Lua之前,我们需要对这门语言有一个初步的了解。Lua是一种小型脚本语言,几乎可以在所有操作系统和平台上运行。我们一般不会用lua来处理特别复杂的事务,所以只需要了解lua的一些基本语法即可。Redis出来之后,它的开发者也意识到了开头提到的问题,所以Redis从2.6版本开始支持Lua脚本。新版Redis还支持LuaScriptdebug。感兴趣的小伙伴可以在官网的Documentation中找到相应的介绍和QuickStart。通过Lua脚本,在使用Redis程序时,可以在以下几个方面得到显着改善:减少网络开销:一次请求就可以完成N次网络请求的操作。原来N个请求的逻辑都在Redis服务器上完成,减少了网络往返延迟;原子操作:Redis会将整个脚本作为一个整体执行,中间不会插入其他命令。这是一个重要的功能,所以一定要带上一个小笔记本,牢记在心。至于为什么是原子操作,我们后面再分析;复用:客户端发送的脚本会永久保存在Redis中。这样其他客户端就可以重用这个脚本,而不需要使用代码来完成相同的逻辑。所以现在有一句话:要想学好Redis,必须会LuaScript。3、通过Lua脚本实现比较和设置接下来我们来实现一个简单的比较和设置,通过这个例子来体验一下Lua脚本给Redis的使用带来的新体验。我们先来看看如何让Redis执行Lua脚本。3.1EVALRedis127.0.0.1:6379>EVALscriptnumkeyskey[key...]arg[arg...]Redis的脚本:参数为Lua5.1脚本程序。脚本不必(也不应该)定义为Lua函数。numkeys:用于指定键名参数的个数。key[key...]:从EVAL的第三个参数开始算起,表示脚本中使用的Redis键(key)。在Lua中,可以通过全局变量KEYS数组访问这些键名参数,以1为基地址(KEYS[1]、KEYS[2]等)。arg[arg...]:附加参数,在Lua中通过全局变量ARGV数组访问,访问形式类似于KEYS变量(ARGV[1]、ARGV[2]等)。这是来自官方网站的示例。上面的脚本直接返回输入参数。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)。Redis127.0.0.1:6379>SCRIPTLOADscriptRedis127.0.0.1:6379>EVALSHAsha1numkeyskey[key...]arg[arg...]Redis提供了一个SCRIPTLOAD命令,命令背后的脚本就是Lua脚本。该命令将脚本脚本添加到脚本缓存中,但不会立即执行脚本。执行命令后Redis会返回一个SHA1字符串,可以执行第二条EVALSHA命令。应该注意的是,脚本可以无限期地保留在缓存中,直到执行SCRIPTFLUSH。让我们看看效果。Redis也支持直接执行Lua脚本文件。先写一个Lua脚本存起来。然后调用Redis-cli–eval命令。redis-cli–eval命令语法与原来的eval语法基本一致。3.3使用Lua脚本实现compareandsetcompareandset的实现逻辑如下:首先获取Redis中指定key的值,然后与给定的值进行比较:若相等,则将key设置为目标值并返回一个标识符;如果不相等,什么都不做并返回一个标识符。ifRedis.call('get',KEYS[1])==ARGV[1]thenRedis.call('set',KEYS[1],ARGV[2]);return1elsereturn0end让我们测试这个脚本。先向Redis的指定键compareAndSet:key写入一个value值。在Redis中执行lua脚本。可以看到,第一次执行返回1,表示修改成功;当使用原参数执行时,返回0,表示没有修改。让我们再次查询compareAndSet:key键。可以看到keycompareAndSet:key已经改成了new_value。4.总结我们通过lua脚本实现了一个简单的compareAndSet操作。让我们用这个例子来验证开头提到的特性。减少网络开销:在不使用脚本的情况下,我们至少需要与Redis交互两次才能实现一个compareAndSet,而现在只需要执行一次操作即可完成;原子操作:得益于Redis的设计,Redis会将整个脚本作为一个整体Execute,中间不会被其他命令插入。因此,在编写脚本的过程中无需担心竞争条件,也无需使用事务。感兴趣的可以自行百度或者等待后续文章的后续更新;复用:可以将一系列操作封装成一个Lua脚本,存储在文件或Redis中,下次使用时直接调用即可。看完本文,希望你对Redis+Lua有一定的了解,能够使用脚本完成一些简单的复合操作。后续还会继续更新一些基于Lua脚本+java程序的分布式数据结构,比如延迟队列、可重入锁等,有兴趣的小伙伴可以持续关注。【本文为栏目机构易新科技原创文章,微信公众号“易新科技(id:CE_TECH)”点击此处查看作者更多好文