一、简介

redis操作时单线程的,平常如果想要redis原子性操作的话,可以使用incrBy()和decrBy()方法进行原子性的加减,但是对于事务性的逻辑操作,没有办法实现原子性,Redis 使用单个 Lua 解释器去运行所有脚本,当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行,因此,lua脚本需要运行的使用比较快,不会妨碍其它lua脚本执行

二、内容说明

redis命令

JedisClusterTemplate方法

作用

EVAL script numkeys key [key ...] arg [arg ...] eval() 执行lua脚本
EVALSHA sha1 numkeys key [key ...] arg [arg ...] evalsha() 执行lua脚本对应的缓存值
SCRIPT EXISTS script [script ...] scriptExists() 判断脚本是否已经添加到缓存中去了,1代表已经添加,0代表没有添加
SCRIPT FLUSH scriptFlush() 清除lua脚本缓存
SCRIPT KILL scriptKill() 杀死当前正在运行的 Lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效,防止lua脚本死循环
SCRIPT LOAD script scriptLoad() 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本,eval是执行并添加缓存

1.eval命令

eval script numkeys key [key ...] arg [arg ...]
#参数说明
#script:它会被运行在 Redis 服务器上下文中,这段脚本不必(也不应该)定义为一个 Lua 函数。
#numkeys:用于指定键名参数的个数。
#key:键名参数,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
#arg:全局变量,可以在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)

使用样例如下:执行脚本,,KEYS[1]对应foo,ARGV[1]对应第二个1,ARGV[2]对应100,角标[1]对应第1个参数,表示一个key,redis.call里面第一个参数redis命令,之后是该命令对应的key-value

--如果当前foo,对应的值减去1之后,小于0,那么就给它加上100返回,否则直接返回减去1值后的值
eval
"if redis.call('decrBy',KEYS[1],ARGV[1]) < 0 thenreturn redis.call('incrBy',KEYS[1],ARGV[2])
elsereturn redis.call('get',KEYS[1])
end"
1 foo 1 100

执行结果如下:

注意:脚本里使用的所有键都应该由 KEYS 数组来传递,变量入参通过ARGV数组传递

通过redis的eval命令来实现,使用 EVAL 命令对 Lua 脚本进行求值。在lua脚本中可以通过两个不同的函数调用redis命令,分别是:redis.call()redis.pcall(),这两者方法对于错误的处理方式不同

redis.call():redis.call关键字执行redis命令,在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,错误的输出信息会说明错误造成的原因

redis.pcall(): 出错时并不引发(raise)错误,而是返回一个带 err 域的 Lua 表(table),用于表示错误

eval 命令会在每次执行脚本的时候都发送一次脚本主体(script body),每次都需要重新编译脚本,会有一定的损耗

2.evalsha命令

evalsha命令和eval一样,但它可以使用脚本生成的缓存sha来执行,出现错误是,会使用eval命令执行lua脚本

#格式
evalsha sha1 numkeys key [key ...] arg [arg ...]

(1)Redis 保证所有被运行过的脚本都会被永久保存在脚本缓存当中,当 eval命令在一个 Redis 实例上成功执行某个脚本之后,随后针对这个脚本的所有 evalsha命令都会成功执行,

(2)清空lua脚本方法需要使用 script flush命令

(3)不能使用全局性的脚本变量

3.其他命令

script load加载脚本到缓存中,但不立即执行,script exists判断是否存在该缓存,script flush清除所有lua脚本缓存,script kill杀死正在执行中的命令

三、Java中使用Jedis操作

1.定义lua脚本

这里代表 key的value当前值减去ARGV[1],如果减去ARGV[1]之后小于0的话则返回原来值,否则返回减去之后的值

public static final String LUA = "if redis.call('decrBy',KEYS[1],ARGV[1]) < 0 then\n" +"  return redis.call('incrBy',KEYS[1],ARGV[1])\n" +"  else\n" +"   return redis.call('get',KEYS[1])\n" +"end ";

2.加载lua脚本(可省略)

String key = "demo";
jedisClusterTemplate.set(key, "2");
log.info("加载脚本lua脚本:lua script={}", LUA);
jedisClusterTemplate.scriptLoad(LUA, key);

3.使用eval执行lua脚本

执行jedisClusterTemplate中的eval方法,传入lua脚本,key的个数,只有接入对应参数,参数对应前面2.1里面redis用法

Redis Lua脚本实现原子性操作相关推荐

  1. 一网打尽Redis Lua脚本并发原子组合操作

    1. 前言 Redis 是高性能的 KV 内存数据库,除了做缓存中间件的基本作用外还有很多用途,比如胖哥以前分享的Redis GEO 地理位置信息计算.Redis 提供了丰富的命令来供我们使用以实现一 ...

  2. Redis Lua 脚本常用操作总结及实现 CAS 操作

    一.什么是 Lua ?   Lua 是一个小巧的脚本语言.它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由 R ...

  3. 高并发-【抢红包案例】之四:使用Redis+Lua脚本实现抢红包并异步持久化到数据库

    文章目录 导读 概述 实现步骤 注解方式配置 Redis lua脚本和异步持久化功能的开发 Service层添加Redis抢红包的逻辑 Controller层新增路由方法 构造模拟数据,测试 代码 总 ...

  4. Redis Lua脚本的详细介绍以及使用入门

    Redis Lua脚本的详细介绍以及使用入门. 文章目录 Redis Lua脚本的引入 开源软件的可扩展性 Redis的扩展性脚本 Redis Lua脚本的基本使用 通过EVAL命令执行Lua脚本 通 ...

  5. Redis Lua脚本中学教程(下)

    在中学教程的上半部分我们介绍了Redis Lua相关的命令,没有看过或者忘记的同学可以步行前往直接使用机票Redis Lua脚本中学教程(上).今天我们来简单学习一下Lua的语法. 在介绍Lua语法之 ...

  6. Redis Lua脚本大学教程

    前面我们已经把Redis Lua相关的基础都介绍过了,如果你可以编写一些简单的Lua脚本,恭喜你已经可以从Lua中学毕业了. 在大学课程中,我们主要学习Lua脚本调试和Redis中Lua执行原理两部分 ...

  7. 基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性

    为了保证数据的争用安全,通常要采用锁机制控制. 如果是单应用部署,直接通过synchronized关键字修改方法,就能解决,但是如果是分布式的部署 该方法就不能解决这个问题啦,此时就引出了一个分布式锁 ...

  8. 深入分析 Redis Lua 脚本运行原理

    Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题.Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向服务器发送 lua 脚本来执 ...

  9. Redis Lua脚本中学教程(上)

    失踪人口回来啦! 有读者问我为什么这么久都没有出Redis Lua中学教程,表示村头厕所已经好久没有纸了.其实我早就要写这篇中学教程了,奈何最近太忙了,就一拖再拖,直到今天我终于又开始动笔了.忘记Lu ...

最新文章

  1. 字节跳动ClickHouse在用户增长分析场景的应用
  2. nginx解析php文件设置_nginx 解析php conf配置文件
  3. ITK:查找图像的更高导数
  4. 用Visual Studio Code调试nodejs
  5. controller方法要trycatch吗_拜托,别再满屏try catch了,试试统一异常处理吧
  6. 关于CentOS-6的默认带的mysql启动和安装问题
  7. JUnit4常用的注解
  8. html下移,jQuery实现元素的上移下移删除
  9. LCD的控制驱动及基与MCU接口的特点
  10. python进行删除标点符号
  11. kail 安装小企鹅输入法
  12. 台式计算机usb口不能用,电脑usb接口没反应是什么原因?电脑usb接口没反应解决方法...
  13. 设计模式常用的七大原则之③【依赖倒转】原则
  14. 如何安装imgaug
  15. 解决fullpage尾屏半屏问题
  16. Android kotlin上传头像实现
  17. java oval 入门_java开源验证框架OVAL-Go语言中文社区
  18. 百度新闻向左 Google新闻向右【新媒体】
  19. seurattogiotto中的python环境设置
  20. 数据产品经理---如何设计指标字典

热门文章

  1. html5如何传递值,如何将var中的值传递到另一个var
  2. 比亚迪汉鸿蒙系统测评_余承东携鸿蒙OS亲自站台,比亚迪汉凭借刀片电池能否对抗Model 3?...
  3. 5g虚拟技术旅游_5G赋能VR产业变革
  4. 饥荒海难机器人怎么用_饥荒开发商又一款神作,难到吐血的生存游戏!
  5. 三十一、Python读写docx文件
  6. django自定义过滤器及模板标签
  7. sklearn特征的提取(上)
  8. 波束形成、回声消除、声源定位及端到端等语音信号处理算法
  9. 构想:中文文本标注工具(内附多个开源文本标注工具)
  10. pytorch基本数学运算:加法 减法 乘法 除法 指数 对数 绝对值