最近琢磨分布式锁时接触到的知识点,简单记一下。

1. Redis中的Lua

 Redis支持Lua,代码直接发送完整脚本即可。基本语法(redis客户端可以直接执行):

> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

注:{KEYS[1],KEYS[2],ARGV[1],ARGV[2]}都是一系列的 key -value 的占位符(KEYSARGV都是全局变量),根据实际情况编写,可以是很多这样形式变量。后面的2 key1 first key2 second,首位数字2表示的是 Key 的数量,后面的变量和前面的占位符意义对应(key1对应于KEYS[1],执行时key1自动注入)

2. 利用Lua操作Redis

 上述执行借用redis的客户端执行lua脚本,要真正对redis进行读写操作,需要调用redis.call()函数或者redis.pcall()函数,这两个函数比较相似,但如果发生错误时,redis.call()函数将引发一个Lua错误,这又将迫使EVAL向命令调用者返回错误;而redis.pcall()则会捕获异常并返回Lua异常的某个信息码。使用示例如下:

# 同样的最后的 0 表示的全局变量 KEYS 中有0个元素 key
eval "return redis.call('set','foo','bar')" 0

向redis中写入一个 key 为foo,value为bar的对象。但eval命令将会验证这条命令的语法,具体的验证方式为:将脚本中所有的key替换为全局变量数据KEYS,所以最终验证语法的脚本为eval "return redis.call('set',KEYS[1],'bar')" 1 foo

上述2种方式调用出错的情况

当使用redis.call()函数或者redis.pcall()函数操作redis时,Redis返回的值对象将会被转换成Lua语言中的数据类再返回。相似了当执行redis.call()函数或者redis.pcall()函数时,Lua变量的数据类型将会被转成Redis中支持的数据类型(string,list,set,hash,Sorted Set)。

3. Lua脚本的原子性

 Lua脚本在redis中的操作是原子性,redsi使用同一个Lua解释器执行脚本中的所有命令,Redis本身也保证了这个脚本执行的原子性:当该脚本在执行时间区间内,不会有其他的脚本或者命令执行。所以使用Lua执行一个慢脚本是一个很扯的事情(除非你很清楚这样的慢脚本是十分有必要的),一般情况下,因为脚本的开销非常低会很快。

4. 关于 EVALSHA

EVAL命令会迫使我们反复发送脚本,但Redis不需要每次都重新编译脚本(因为自己内部的缓存机制),但每次发送脚本需要耗费额外的带宽在很多场景中并不是一种友好的方式。另一方面,使用特殊命令或者通过redis.conf来定义命令也会有一些问题:

  • 不同的实例可能有该命令的不同实现;
  • 如果需要确保所有的实例都包含所给的命令,那部署将会很困难,尤其是在分布式环境中;
  • 读取应用程序代码后,由于应用程序将调用定义在服务器端的命令,因此完整的语义可能不清楚;

为了解决上述的问题,Redis实现了EVALSHA命令,EVALSHA命令和EVAL命令很相似,但EVALSHA没有以脚本本身作为第一个参数,而是将该脚本的SHA1摘要作为第一个参数,具体行为:

  1. 如果服务端仍然记得和 SHA1 摘要匹配的脚本,那直接执行脚本;
  2. 如果忘了和 SHA1 摘要匹配的脚本,那将会抛出一个异常告诉客户端,使用EVAL命令来执行,不要使用EVALSHA

所以客户端最好的方式还是使用EVALSHA命令来执行脚本(即使客户端使用EVAL,脚本实际已经被服务端看到,如果返回NOSCRIPT的错误就会在使用EVAL命令)。

脚本缓存机制:已执行的脚本会永远存在于所执行的Redis实例的脚本缓存中。这就意味着如果一个EVAL在Redis实例中执行,则后续所有的EVALSHA命令都会调用成功。显式的调用SCRIPT FLUSH将会刷新脚本缓存,也会清除到目前为止所有执行过的脚本(这也是清除脚本缓存的唯一方式)。

5. 常用SCRIPT 命令

 Redis中脚本本身操作的命令有:

  • SCRIPT FLUSH:清除Redis中到目前为止所有执行过的脚本缓存;
  • SCRIPT EXISTS sha1 sha2 ... shaN:验证脚本是否存在缓存中,返回会对应个数的0(缓存中不存在)和1(缓存中存在);
  • SCRIPT LOAD script:将脚本注册进入服务端的存储而不需要执行,确保EVALSHA可以找到对应的脚本正常执行,返回该脚本的SHA1加密值;
  • SCRIPT KILL:打断正在执行的脚本;
# 注册脚本
script load "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
# 返回sha1值
"a42059b356c875f0717db19a51f6aaca9ae659ea"

6. 脚本本地化

 可以本地编写Lua脚本,直接通过客户端调用redis-cli --eval xxx.lua执行执行xxx.lua脚本。如实际Linux中操作经常用到redis-cli -h hostname -p port -a password SCRIPT LOAD "$(cat lua_script_file_location)"类似的命令将指定脚本缓存到Redis的服务端,已便下次直接通过返回的摘要直接执行脚本。
t lua_script_file_location)"`类似的命令将指定脚本缓存到Redis的服务端,已便下次直接通过返回的摘要直接执行脚本。

[转帖]Redis中的Lua脚本的更多相关文章

  1. 在redis中使用lua脚本

    在实际工作过程中,可以使用lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能. 不过lua也会有很多限制,在使用的时候要注意. 在Redis中执行Lu ...

  2. redis中使用lua脚本

    lua脚本 Lua是一个高效的轻量级脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能 使用脚本的好处 1.减少网络开销,在Lua脚 ...

  3. 新姿势!Redis中调用Lua脚本以实现原子性操作

    背景:有一服务提供者Leader,有多个消息订阅者Workers.Leader是一个排队程序,维护了一个用户队列,当某个资源空闲下来并被分配至队列中的用户时,Leader会向订阅者推送消息(消息带有唯 ...

  4. Redis中的原子操作(2)-redis中使用Lua脚本保证命令原子性

    Redis 如何应对并发访问 使用 Lua 脚本 Redis 中如何使用 Lua 脚本 EVAL EVALSHA SCRIPT 命令 SCRIPT LOAD SCRIPT EXISTS SCRIPT ...

  5. 在redis中使用lua脚本让你的灵活性提高5个逼格

    在redis的官网上洋洋洒洒的大概提供了200多个命令,貌似看起来很多,但是这些都是别人预先给你定义好的,但你却不能按照自己的意图进行定制, 所以是不是感觉自己还是有一种被束缚的感觉,有这个感觉就对了 ...

  6. redis中使用java脚本实现分布式锁

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/115.html?1455860390 edis被大量用在分布式的环境中,自 ...

  7. Redis进阶实践之十九 Redis如何使用lua脚本

    一.引言               redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入 ...

  8. C++中嵌入Lua脚本环境搭建

    第一步(环境准备工作): 工具: ●LuaForWindows_v5.1.4-46.exe傻瓜式安装. 作用:此工具可以在windows环境下编译运行Lua脚本程序.安装完成后会有两个图标:Lua和S ...

  9. 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!

    [COCOS2DX-LUA 脚本开发之一]在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途! 分类: [Cocos2dx Lua 脚本开发 ] 2012-04-1 ...

  10. 怎样在Cocos2d-x中使用Lua脚本

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u013321328/article/details/25699545 笔者使用的是Cocos2d-x ...

随机推荐

  1. 如何只用一个小时定制一个行业AI 模型?

    摘要:华为云通过ModelArts Pro提供更多的行业套件和工作流可以供用户直接使用,进一步降低AI行业落地难度,让传统企业客户无需掌握算法知识亦可实现自身业务与AI技术的融合. 去年,在华为开发者 ...

  2. Solon2 开发之IoC,十、Bean 在容器的两层信息

    1.剖析 Bean 的装包与注册过程 比如,用配置器装配一个 Bean (本质是装配出一个 BeanWrap,并自动注册到容器): @Configuration public class Config ...

  3. 微服务系列-如何使用 RestTemplate 进行 Spring Boot 微服务通信示例

    概述 下面我们将学习如何创建多个 Spring boot 微服务以及如何使用 RestTemplate 类在多个微服务之间进行同步通信. 微服务通信有两种风格: 同步通讯 异步通信 同步通讯 在同步通 ...

  4. JupyterLab 桌面版 !!!

    JupyterLab 是广受欢迎的 Jupyter Notebook「新」界面.它是一个交互式的开发环境,可用于 notebook.代码或数据,因此它的扩展性非常强. 用户可以使用它编写 notebo ...

  5. 高数 | Dirichlet 积分

    在分析学中,Dirichlet 积分 是如下形式的 无穷限积分 \[\int_{0}^{+\infty} \frac{\sin x}{x} \mathrm{~d} x \] 它是条件收敛的,且收敛到 ...

  6. AtCoder Beginner Contest 199 游记(AB水题,C字符串操作,D搜索,E状压)

    A - Square Inequality 水题 B - Intersection 水题,就是找公共区间,维护一下 Lmax,Rmin即可 void solve() { int n, a, b; in ...

  7. xapian 搜索引擎介绍与使用入门

    Xapian 是一个开源搜索引擎库,使用 C++ 编写,并提供绑定(bindings )以允许从多种编程语言使用.它是一个高度适应性的工具包,允许开发人员轻松地将高级索引和搜索功能添加到自己的应用程序 ...

  8. 技术分享 | 不同格式标准SBOM清单横评:SPDX、CDX和DSDX

    为了保证安全性.降低开发.采购及维护的相关成本,复杂动态的现代软件供应链对软件资产透明度提出了更高的要求.使用清晰的软件物料清单(SBOM)收集和共享信息,并在此基础上进行漏洞.许可证和授权管理等,可 ...

  9. 如何绕过某讯手游保护系统并从内存中获取Unity3D引擎的Dll文件

    ​ 某讯的手游保护系统用的都是一套,在其官宣的手游加固功能中有一项宣传是对比较热门的Unity3d引擎的手游保护方案,其中对Dll文件的保护介绍如下, "Dll加固混淆针对Unity游戏,对 ...

  10. mysql 使用 trim去不掉空格 解决

    使用mysql8.0时 发现 有几个空字符串怎么也过滤不掉,使用 is not null.trim()<>''.length()>=1都不行,最后查了一些资料说 trim只能去除半角 ...