Redis从2.6版本开始引入对Lua脚本的支持,通过在服务器中嵌入Lua环境,Redis客户端可以使用Lua脚本,直接在服务端原子的执行多个Redis命令。
lua脚本的好处:
  减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延
  原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
  复用。客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
 

1.在redis里使用EVAL和EVALSHA

可以使用EVAL命令对输入的脚本进行接受结果
  如:   Eval "return 1+1" 0
  ——>(integer)2
而使用evalsha 命令则可以根据脚本的SHA1校验和对接受脚本的结果,但这个命令要求校验和对应的脚本必须至少执行过一次或者这个校验和对应的脚本被scriptload加载过。
 
  如:
  script load "return 1+2" // 会返回一个sha校验
  ->"e13c398af9f2658ef7050acf3b266f87cXXXXXX"
  ->evalsha "e13c398af9f2658ef7050acf3b266f87cXXXXXX" 0
  ->(integer) 3
在脚本比较长的情况下,如果每次调用脚本都需要将整个脚本传给Redis会占用较多的带宽。为了解决这个问题,Redis提供了EVALSHA命令,允许开发者通过脚本内容的SHA1摘要来执行脚本,该命令的用法和EVAL一样,只不过是将脚本内容替换成脚本内容的SHA1摘要。
 
 

2.伪客户端

因为执行Redis命令必须有相应的客户端状态,所以为了执行Lua脚本中包含的Redis命令,Redis服务器专门为Lua环境创建了一个伪客户端,并由这个伪客户端负责处理Lua脚本中包含的所有Redis命令。
Lua脚本使用redis.call函数或者redis.pcall函数执行一个Redis命令,需要完成以下步骤:
  (1).Lua环境将redis.call函数或者redis.pcall函数想要执行的命令传给伪客户端。
  (2).伪客户端将脚本想要执行的命令传给命令执行器。
  (3).命令执行器执行伪客户端传给它的命令,并将命令的执行结果返回给伪客户端。
  (4).伪客户端接收命令执行器返回的命令结果,并将这个命令结果返回给Lua环境。
  (5).Lua环境在接收到命令结果以后,将该结果返回给redis.call函数或者redis.pcall函数。
  (6).接收到结果的redis.call函数或者redis.pcall函数会将命令结果作为函数返回值给脚本中的调用者。

3.lua_scripts字典

除了伪客户端之外,Redis服务器伪Lua环境创建了另一个协作组件是lua_scripts字典,这个字典的键为某个Lua脚本的SHA1校验和(checksum),而字典的值则是SHA1校验和对应的Lua脚本:
如:
  script load "retrun 1+1"
  -> "e13c398af9f2658ef7050acf3b266f87cXXXXXX"
 
这个返回值和载入的脚本是一一对应关系,这个校验值会存在script字典里。服务器会将所有被eval命令执行过的lua脚本,以及被scriptload载入过的脚本保存在字典里。
 
lua_script字典的两个作用
  (1).一个是实现script exists命令
  (2).实现脚本复制功能。
 

4.EVAL命令的实现

EVAL命令的执行过程可以分为以下三个步骤:
  (1)根据客户端给定的Lua脚本,在Lua环境中定义一个Lua函数。
  (2)将客户端给定的脚本保存到lua_scripts字典,等待将来进一步说明。
  (3)执行刚刚才Lua环境中定义的函数,以此来执行客户端给定的Lua脚本。

5.Redis管理Lua脚本

  (1).script load 
  此命令用于将Lua脚本加载到Redis内存中 
  (2).script exists 
  scripts exists sha1 [sha1 …] 
  此命令用于判断sha1是否已经加载到Redis内存中 
  (3).script flush 
  此命令用于清除Redis内存已经加载的所有Lua脚本,在执行script flush后,sha1不复存在。 
  (4).script kill 
  此命令用于杀掉正在执行的Lua脚本。
  6.redis.call和pcall

6.redsi编写lua脚本

语法:
  $ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...
  --eval,告诉redis-cli读取并运行后面的lua脚本
  path/to/redis.lua,是lua脚本的位置
  KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取
  ARGV[1] ARGV[2],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。
注意:
  KEYS和ARGV中间的 ',' 两边的空格,不能省略。
 
如:
  redis.call('set', 'foo', 'bar')
  local value=redis.call('get', 'foo') --value的值为bar
redis.pcall函数,功能与redis.call相同,唯一的区别是当命令执行出错时,redis.pcall会记录错误并继续执行,而redis.call会直接返回错误,不会继续执行。
 
 
 

在redis里面使用lua的更多相关文章

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

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

  2. 在redis中使用lua脚本

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

  3. redis中使用lua脚本

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

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

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

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

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

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

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

  7. StackExchange.Redis加载Lua脚本进行模糊查询的批量删除和修改

    前言 使用StackExchange.Redis没有直接相关的方法进行模糊查询的批量删除和修改操作,虽然可以通过Scan相关的方法进行模糊查询,例如:HashScan("hashkey&qu ...

  8. Php+Redis 实现Redis提供的lua脚本功能

    <?php require_once "predis-0.8/autoload.php"; $config['schema'] = 'tcp'; $config['host' ...

  9. python redis客户端使用lua脚本

    有一个需求,为一个key设置一个field存储时间戳,每当有新数据,判断新数据时间戳是否大于之前的时间戳,如果是,更新时间戳,由于依赖中间执行结果,所以使用lua减少客户端和服务端通信次数. #!/u ...

随机推荐

  1. python 安装第三方包

    python环境是Anaconda3安装的,由于项目需要用到git的第三方包,但是在conda自带的环境中没有. 例如使用jieba分词库. 安装的三种方式: (1)全自动安装:`easy_insta ...

  2. Django 之 文件配置、pycharm及django连接数据库、创表及表的增删改查02

    目录 创建项目后的文件夹配置 静态文件配置 接口前缀动态绑定 form表单回顾 根据请求方式的不同,返回前端不同的信息 pycharm 连接MYSQL数据库 Django 连接MYSQL数据库的配置 ...

  3. python基础语法-Ⅲ

    Python注释 python中单行注释采用 # 开头. 实例 输出结果: 注释可以在语句或表达式行末: python 中多行注释使用三个单引号(''')或三个双引号(""&quo ...

  4. JS 全局作用域和局部作用域

    一.作用域 1.什么是作用域(Scope) 通常来说,一段程序代码中所用到的名字不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域. JS作用域:就是代码名字(变量)作用的范围 ...

  5. 《剑指offer》算法题第一天

    按照个人计划,从今天开始做<剑指offer>上面的算法题,练习平台为牛客网,上面对每道题都有充分的测试实例,感觉还是很不错的.今天下午做了四道题,分别为: 1. 二叉树的深度(书55题) ...

  6. springboot + mybatis sql日志

    #mapper sql日志 替换成你的mapper接口所在的包名 logging.level.com.example.dao=debug

  7. 高性能JavaScript之加载和执行

    JS在浏览器中的性能,可以认为是开发者所面临的最重要的可行性问题.这个问题因JS的阻塞特性变得复杂,也就是说当浏览器在执行JS代码时,不能同时做其他任何事情.事实上,大多数浏览器都使用单一进程来处理U ...

  8. Apache配置转发

    第一种: LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_htt ...

  9. java.sql.SQLSyntaxErrorException: ORA-01795: 列表中的最大表达式数为 1000

    后台报了一些异常日志,查阅后发现在 oracle 数据库中使用 in 关键字条件不能超过 1000 个,当时写查询语句时没有关注这个问题 总结一下解决方法 1.分多次查询,对查询要求不高的话.把入参的 ...

  10. 全面解读php-流程控制

    一.PHP遍历数组的三种方式 示例: $arr = [1, 2, 3 4, 'five' => 5]; 1.for ()  for循环只能用于遍历纯索引数组!如果存在关联数组,count统计时会 ...