Redis调用Lua脚本及使用场景快速掌握

目录
  • 一、阅读本文前置条件
  • 二、为什么需要Lua脚本
  • 三、学点Lua语法
    • 3.1.一个简单的例子
    • 3.2.仔细看下Lua脚本里的内容
    • 3.3.复杂点的例子
  • 四、Lua脚本预加载
  • 五、一个修改JSON数据的例子?
  • 六、总结

一、阅读本文前置条件

可以遵循这个链接中的方法在操作系统上安装 Redis

如果你对redis命令不熟悉,查看《Redis 命令引用

二、为什么需要Lua脚本

简而言之:Lua脚本带来性能的提升。

很多应用的服务任务包含多步redis操作以及使用多个redis命令,这时你可以使用Redis结合Lua脚本,会为你的应用带来更好的性能。

另外包含在一个Lua脚本里面的redis命令具备原子性,当你面对高并发场景下的redis数据库操作时,可以有效避免多线程操作产生脏数据。

三、学点Lua语法

说了那么多,Lua不会怎么办?不要慌!Lua其实很简单,如果你曾经学习过任何一门编程语言,学习Lua都非常简单。下面给大家举几个例子学习一下:

3.1.一个简单的例子

Lua脚本通过各种语言的redis客户端都可以调用,我们就简单一点使用redis-cli
看下面的redis命令行:

eval "redis.call('set', KEYS[1], ARGV[1])" 1 key:name value

EVAL命令行后面跟着的是Lua脚本:"redis.call('set', KEYS[1], ARGV[1])",放到编程语言里面就是一段字符串,跟在Lua脚本字符串后面的三个参数依次是:

  • redis Lua脚本所需要的KEYS的数量 ,只有一个KEYS[1],所以紧跟脚本之后的参数值是1
  • Lua 脚本需要的参数KEYS[1]的参数值,在我们的例子中值为key:name
  • Lua 脚本需要的参数ARGV[1]的参数值,在我们的例子中值为value

Lua脚本中包括两组参数:KEYS[]和ARGV[],两个数组下标从1开始。一个值得去遵守的最佳实践是:把redis操作所需的key通过KEYS进行参数传递,其他的Lua脚本所需的参数通过ARGV进行传递。

上面的脚本执行完成之后,我们使用下面的Lua脚本来进行验证,如果该脚本的返回值是”value”,与我们之前设置的key:name的值相同,则表示我们的Lua脚本被正确执行了。

eval "return redis.call('get', KEYS[1])" 1 key:name

3.2.仔细看下Lua脚本里的内容

我们的第一个Lua脚本只包含一条语句,调用redis.call

redis.call('set', KEYS[1], ARGV[1])

所以在Lua脚本里面可以通过redis.call执行redis命令,call方法的第一个参数就是redis命令的名称,因为我们调用的是redis 的set命令,所以需要传递key和value两个参数。

我们第二个脚本不只是执行了一个脚本,因为执行get命令还返回了执行结果。注意脚本中有一个return 关键字。

eval "return redis.call('get', KEYS[1])" 1 key:name

当然如果只是上面的这么简单的Lua脚本,还不如直接使用命令行更方便。我们实际使用到的Lua脚本会比上面的复杂,上面的Lua脚本只是一个Hello World。

3.3. 复杂点的例子

我曾使用Lua脚本从一个hash map里面按照一定的顺序获取若干key对应的值。对应的顺序在一个zset排序集合中进行保存,数据设置及排序可以通过下面的完成。

# 设置hkeys为键Hash值
hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6
# 建一个order为键的集合,并给出顺序
zadd order 1 key:3 2 key:1 3 key:2

如果不知道hmset和zadd命令的作用,可以参考hmsetzadd

执行下面的Lua脚本

eval "local order = redis.call('zrange', KEYS[1], 0, -1); return redis.call('hmget',KEYS[2],unpack(order));" 2 order hkeys

你将看到如下的输出结果

“value:3”
“value:1”
“value:2”

  • 通过zrange取出order集合里面的数据,即:[ key:3 , key:1 , key:2]
  • 然后通过unpack函数将[ key:3 , key:1 ,key:2] 转成 key:3 key:1 key:2
  • 最后执行 hmget hkeys key:3 key:1 key:2,所以得到上面的输出结果

四、Lua脚本预加载

Redis可以对Lua脚本进行预加载,可以通过script load命令把Lua脚本预加载到redis里面。

script load "return redis.call('get', KEYS[1])"

预加载完成之后,你会看到下面的一段输出

“4e6d8fc8bb01276962cce5371fa795a7763657ae”

这是一个具有唯一性的hash字符串,这个hash就代表着我们刚刚预加载的Lua脚本,我们可以通过EVALSHA命令执行该脚本。如:

evalsha 4e6d8fc8bb01276962cce5371fa795a7763657ae 1 key:name

执行的结果与下面的是一致的。

eval "return redis.call('get', KEYS[1])" 1 key:name

五、一个修改 JSON数据的例子?

有些开发人员有的时候可能会将JSON数据保存在Redis里面,我们先不说这样做是不是一种好的方式,我们只来谈一下如何通过Lua脚本修改JSON数据。

正常情况下,你需要修改一个JSON Object,你需要把它从redis里面查询回来,解析它,修改key值,然后再将它序列化保存到redis里面。这样做有几个问题:

高并发场景下无法保证原子性,另一个线程可以在当前线程获取和设置Object操作之间更改这个JSON数据。在这种情况下,将丢失更新。

性能问题。如果您经常进行这样的更改并且JSON数据相当大,这可能会成为应用的性能瓶颈。因为你经常性的进行取数据,存数据。

通过在 Lua 中实现上面逻辑,因为redis的Lua脚本是在服务端执行的,一方面可以保证操作的原子性,解决高并发丢失更新的问题,另一方面节省网络传输同时提升性能。

下面我们向redis里面保存一个测试JSON 字符串:obj

set obj '{"a":"foo","b":"bar"}'

现在,让我们运行我们的脚本:

EVAL 'local obj = redis.call("get",KEYS[1]); local obj2 = string.gsub(obj,"(" .. ARGV[1] .. "\":)([^,}]+)", "%1" .. ARGV[2]);  return redis.call("set",KEYS[1],obj2);' 1 obj b bar2

local obj = redis.call("get",KEYS[1]);

其中KEYS[1]=obj,所以返回值

obj= '{"a":"foo","b":"bar"}'

local obj2 = string.gsub(obj,"(" .. ARGV[1] .. "\":)([^,}]+)", "%1" .. ARGV[2]);

是Lua脚本的字符串连接符号;我们使用 RegEx 模式来匹配密钥并替换其值,如果对表达式不熟悉,自行补课;"%1"表示第一个被匹配的子串,"%1" … ARGV[2] 等于 “b”:“bar2”,并使用gsub进行替换。

最后将结果返回,obj的JSON对象的结果如下:

{"a":"foo","b":"bar2"}

六、总结

我建议只有在你能证明它能带来更好的性能时才使用Lua脚本。如果你只是想要保证redis操作原子性,那么可以使用transactions事务。不一定非要使用Lua脚本。

此外redis Lua脚本不应太长。因为当脚本运行时相当于为被操作对象加锁,其他操作都在等待它完成。如果Lua脚本需要相当长的时间执行,则可能会导致瓶颈而不是提高性能。Lua脚本在达到超时后停止(默认情况下为 5 秒)。

以上就是Redis调用Lua脚本及使用场景快速掌握的详细内容,更多关于Redis调用Lua脚本使用场景的资料请关注我们其它相关文章!

(0)

相关推荐

  • Redis如何使用lua脚本实例教程

    前言 在redis的官网上洋洋洒洒的大概提供了200多个命令,貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制, 所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了... 说来也巧,redis的大老板给了你解决这种问题的方法,那就是Lua脚本,而且redis的最新版本也支持Lua Script debug,这应该也是未来Redis的一 个发展趋势,要想学好Redis,必会Lua Script... 下面话不多说了,来一起看看详细的介绍吧 版本:自2.6.

  • redis中如何使用lua脚本让你的灵活性提高5个逼格详解

    前言 在实际工作过程中,可以使用lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能. 然而在redis的官网上洋洋洒洒的大概提供了200多个命令,貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制, 所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了... 一:Lua脚本 说来也巧,redis的大老板给了你解决这种问题的方法,那就是Lua脚本,而且redis的最新版本也支持Lua Script debug,

  • redis中lua脚本的简单使用

    一.背景 在使用redis的过程中,发现有些时候需要原子性去操作redis命令,而redis的lua脚本正好可以实现这一功能.比如: 扣减库存操作.限流操作等等. redis的pipelining虽然也可以一次执行一组命令,但是如果在这一组命令的执行过程中,需要根据上一步执行的结果做一些判断,则无法实现. 二.使用lua脚本 Redis中使用的是 Lua 5.1 的脚本规范,同时我们编写的脚本的时候,不需要定义 Lua 函数.同时也不能使用全局变量等等. 1.lua脚本的格式和注意事项 1.格式

  • redis lua脚本实战秒杀和减库存的实现

    目录 前言 1.redisson介绍 2. redis lua脚本编写与执行 3.redis减库存lua脚本 4.实战 4.1 减库存逻辑 4.2 压测 前言 我们都知道redis是高性能高并发系统必不可少的kv中间件,它以高性能,高并发著称,我们常常用它做缓存,将热点数据或者是万年不变的数据缓存到redis中,查询的时候直接查询redis,减轻db的压力,分布式系统中我们也会拿它来做分布式锁,分布式id,幂等来解决一些分布式问题,redis也支持lua脚本,而且能够保证lua脚本执行过程中原子

  • Redis执行Lua脚本的好处与示例代码

    前言 Redis从2.6版本开始引入对Lua脚本的支持,通过在服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务端原子的执行多个Redis命令. 其中,使用EVAL命令可以直接对输入的脚本进行求值: redis>EVAL "return 'hello world'" 0 "hello world" 使用脚本的好处如下: 1.减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成.使用脚本,减少

  • Redis调用Lua脚本及使用场景快速掌握

    目录 一.阅读本文前置条件 二.为什么需要Lua脚本 三.学点Lua语法 3.1.一个简单的例子 3.2.仔细看下Lua脚本里的内容 3.3.复杂点的例子 四.Lua脚本预加载 五.一个修改JSON数据的例子? 六.总结 一.阅读本文前置条件 可以遵循这个链接中的方法在操作系统上安装 Redis 如果你对redis命令不熟悉,查看<Redis 命令引用> 二.为什么需要Lua脚本 简而言之:Lua脚本带来性能的提升. 很多应用的服务任务包含多步redis操作以及使用多个redis命令,这时你可

  • Redis中lua脚本实现及其应用场景

    目录 1. Redis Lua脚本概述 2. Redis Lua脚本的优势 3. Redis Lua脚本的应用场景 4. Redis Lua脚本的使用方法 5. java中使用redis的lua脚本 5.1. 添加Redis依赖 在pom.xml中添加以下依赖: 5.2. 配置Redis连接信息 在application.properties中添加以下配置: 5.3. 定义Redis Lua脚本 5.4. 实现RedisService 5.5. 编写Redis Lua脚本 5.6. 测试Redi

  • SpringBoot通过redisTemplate调用lua脚本并打印调试信息到redis log(方法步骤详解)

    第一次写Lua脚本,并通过springboot的redisTemplate调用脚本,进行脚本与应用的交互.不熟悉真的折腾了好久,现在总结一下学习过程: 第一次写完lua时,想到的就是如何在应用调用脚本的时候,去调试脚本.在网上海搜了一把,能找到的有点相关的寥寥无几. 有一种方法是通过执行redis命令,调用redis客户端,加载lua脚本,然后出现基于命令行调试的交互界面,输入调试命令去调试脚本.如下: 在终端输入命令:redis-cli.exe --ldb --eval LimitLoadTi

  • 详解Redis中Lua脚本的应用和实践

    引言 前段时间组内有个投票的产品,上线前考虑欠缺,导致被刷票严重.后来,通过研究,发现可以通过 redis lua 脚本实现限流,这里将 redis lua 脚本相关的知识分享出来,讲的不到位的地方还望斧正. redis lua 脚本相关命令 这一小节的内容是基本命令,可粗略阅读后跳过,等使用的时候再回来查询 redis 自 2.6.0 加入了 lua 脚本相关的命令,EVAL.EVALSHA.SCRIPT EXISTS.SCRIPT FLUSH.SCRIPT KILL.SCRIPT LOAD,

  • redis执行lua脚本的实现方法

    目录 1. 语法格式 2.类型转换 3.lua脚本 3.1 script命令 3.2 脚本原子性 3.3 脚本缓存和EVALSHA 3.4 全局变量保护 3.5 日志记录 从redis 2.6.0版本开始,redis内置了Lua解释器,并提供了eval命令来解析Lua脚本求值. 1. 语法格式 语法: eval script numkeys keys args 参数: eval - redis提供解析lua脚本的命令          script - lua脚本           numke

  • Redis中Lua脚本的使用和设置超时

    目录 EVAL命令简介 eval格式 特性 执行流程 关于脚本超时 SCRIPT KILL 命令 SHUTDOWN NOSAVE 命令 参考 Redis提供了Lua脚本功能来让用户实现自己的原子命令,但也存在着风险,编写不当的脚本可能阻塞线程导致整个Redis服务不可用. 本文将介绍Redis中Lua脚本的基本用法,以及脚本超时导致的问题和处理方式. EVAL命令简介 eval格式 Redis 提供了命令EVAL来执行Lua脚本,格式如下 EVAL script numkeys key [key

  • redis中lua脚本使用教程

    目录 一.背景 二.使用lua脚本 三.lua和redis数据类型转换 四.lua脚本中输出日志 五.一个简单限流的案例 六.lua脚本的debug 七.参考文档 一.背景 在使用redis的过程中,发现有些时候需要原子性去操作redis命令,而redis的lua脚本正好可以实现这一功能.比如: 扣减库存操作.限流操作等等. redis的pipelining虽然也可以一次执行一组命令,但是如果在这一组命令的执行过程中,需要根据上一步执行的结果做一些判断,则无法实现. 二.使用lua脚本 Redi

  • redis通过lua脚本,获取满足key pattern的所有值方式

    我们知道,redis提供了keys命令去获取所有满足格式的key,如我们键入命令 keys "user*" 将得到所有以user开头的key 然后执行 mget命令可以获取多个key的值,如 但如果满足条件的key过多,我们要将所有key拿到,再用mget去拿到所有值则为相对比较麻烦,因此可以借助xargs redis-cli keys "user*"|xargs redis-cli mget获取到所有key的值 也可以执行lua脚本local keys = red

随机推荐