Redis为什么需要Lua脚本的支持

当应用需要Redis完成一些Redis命令不支持的特性时,要么扩展Redis client或者更甚至编写c扩展Redis server。这都大大造成了应用的实现的难度。在此基础上,Redis通过内置Lua解释器,Redis client可以发起执行Lua脚本,完成特殊的功能需求。

Redis中使用Lua脚本

在Redis中可以通过使用eval和evalsha命令提供对执行Lua的支持。

eval语法:

EVAL script numkeys key [key ...] arg [arg ...]

  • script是lua脚本;
  • numkeys是lua脚本中key的数量;
  • key是多选,即lua脚本中可以操作多个key;
  • arg是多选,供lua基本执行时提供参数;

在lua脚本中可以通过全局变量KEYS和ARGV获取key和arg。KEYS和ARGV都是数组,KEYS[1],KEYS[2],...等等,基数从1开始,按照顺序获取后面的key。ARGV同理。

如:

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

上面返回key1和key2的value,并且返回连个参数。

Redis提供了两个不同的Lua函数调用执行Redis命令:

  • redis.call():当redis命令执行错误时,将会返回错误;
  • redis.pcall:当redis命令执行错误时,将会捕获异常,返回一个带有错误域的Lua表;

如:

127.0.0.1:6380>  lpush foo a
(integer) 1 127.0.0.1:6380> eval "return redis.call('get','foo')" 0
(error) ERR Error running script (call to f_6b1bf486c81ceb7edf3c093f4c48582e38c0e791): @user_script:1: WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6380> EVAL "return redis.pcall('get', 'foo')" 0
(error) WRONGTYPE Operation against a key holding the wrong kind of value

Lua脚本作为一种脚本语言,有自己的数据类型,Redis也有自己的数据类型。在Lua脚本和Redis命令操作的之间切换时,必然会涉及数据类型的转化。

数据类型之间的转换遵循这样一个设计原则:Redis调用Lua解释器执行脚本时,会将Redis类型转化成Lua类型;当Lua脚本执行后,返回值时,会将返回值转化成Redis类型,eval再将Lua脚本返回给client。

具体的对应细节参考:Lua 数据类型和 Redis 数据类型之间转换

Redis中Lua脚本带来的收益

原子性:Redis使用单个Lua解释器去运行所有脚本,并且Redis也保证脚本会以原子性的方式执行:当某个脚本正在运行的时候,不会有其他脚本或Redis命令被执行。这和使用MULTI/EXEC包围的事务很类似。在其他别的客户端看来,脚本的效果要么是不可见的,要么就是已完成的。另一方面,这也意味着,执行一个运行缓慢的脚本并不是一个好主意。写一个跑得很快很顺溜的脚本并不难,因为脚本的运行开销非常少,但是当你不得不使用一些跑得比较慢的脚本时,请小心,因为当这些蜗牛脚本在慢吞吞地运行的时候,其他客户端会因为服务器正忙而无法执行命令。

缓存脚本:eval命令要求每次每次执行lua脚本时,都需要发送lua脚本至服务器,虽然redis缓存机制保证不会重新编译lua脚本,但是每次都传输脚本主体,无疑是消耗带框。为了减少带框,Redis使用evalsha命令发起lua脚本,但是evalsha的第一个参数不是lua脚本,而是脚本所对应的shasum校验和值。

如:

127.0.0.1:6380> set foo bar
OK
127.0.0.1:6380>
127.0.0.1:6380>
127.0.0.1:6380> eval "return redis.call('get','foo')" 0
"bar"
127.0.0.1:6380> evalsha 6b1bf486c81ceb7edf3c093f4c48582e38c0e791 0
"bar"

客户端库的底层实现可以一直乐观地使用 EVALSHA 来代替 EVAL ,并期望着要使用的脚本已经保存在服务器上了,只有当 NOSCRIPT 错误发生时,才使用 EVAL 命令重新发送脚本,这样就可以最大限度地节省带宽。

Redis提供的SCRIPT命令

  • script flush:清除所有脚本缓存;
  • script exists:根据给定的脚本校验和,判断脚本是否存在;
  • script load:将一个脚本载入内存,但是不运行;
  • script kill:杀死一个正在执行的脚本程序;
参考

https://redis.io/commands/eval

Redis(六)Lua脚本的支持的更多相关文章

  1. 快速入门Redis调用Lua脚本及使用场景介绍

    Redis 是一种非常流行的内存数据库,常用于数据缓存与高频数据存储.大多数开发人员可能听说过redis可以运行 Lua 脚本,但是可能不知道redis在什么情况下需要使用到Lua脚本. 一.阅读本文 ...

  2. Redis学习-LUA脚本

    最近在做K线的项目中,需要计算商品的分时数据.为了保证多台机器对同一商品的计算的有序性,所以在Redis中进行计算,同时为了保证在分时数据计算过程的原子性所以使用了LUA脚本,Redis内置了对LUA ...

  3. Redis结合Lua脚本实现高并发原子性操作

    从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis … 案例-实现访问频率限制: 实现访问者 $ip 在一定的时间 $time 内只能访问 $limit 次. 非脚 ...

  4. .Net Core使用分布式缓存Redis:Lua脚本

    一.前言 运行环境window,redis版本3.2.1.此处暂不对Lua进行详细讲解,只从Redis的方面讲解. 二.Redis的Lua脚本 在Redis的2.6版本推出了脚本功能,允许开发者使用L ...

  5. 要想用活Redis,Lua脚本是绕不过去的坎

    前言 Redis 当中提供了许多重要的高级特性,比如发布与订阅,Lua 脚本等.Redis 当中也提供了自增的原子命令,但是假如我们需要同时执行好几个命令的同时又想让这些命令保持原子性,该怎么办呢?这 ...

  6. redis中lua脚本的简单使用

    一.背景 在使用redis的过程中,发现有些时候需要原子性去操作redis命令,而redis的lua脚本正好可以实现这一功能.比如: 扣减库存操作.限流操作等等. redis的pipelining虽然 ...

  7. PHP中使用redis执行lua脚本示例

    摸索了一下在PHP中如何使用redis执行lua脚本,写了一个脚本如下,供以后参考 <?php $redis = new Redis(); #实例化redis类 $redis->conne ...

  8. 【spring boot】【redis】spring boot基于redis的LUA脚本 实现分布式锁

    spring boot基于redis的LUA脚本 实现分布式锁[都是基于redis单点下] 一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁 1.pom.xml &l ...

  9. Redis 的 Lua 脚本支持

    Redis 2.6.0 内置的Lua Script支持,可以在Redis的Server端一次运行大量逻辑. 整个Script默认是在一个事务里的. Script里涉及的所有Key尽量用变量,从外面传入 ...

随机推荐

  1. 关于css中的定位

    关于前端的几种定位方式 近期自己感觉自己对于前端定位的知识还是不是太理解,所以自己就在这里做一个总结 1.元素的定位属性主要包括定位模式和边偏移两部分. 边偏移属性 描述 top       bott ...

  2. JavaScript函数式编程究竟是什么?

    摘要: 理解函数式编程. 作者:前端小智 原文:JS中函数式编程基本原理简介 Fundebug经授权转载,版权归原作者所有. 在长时间学习和使用面向对象编程之后,咱们退一步来考虑系统复杂性. 在做了一 ...

  3. fetch的常见问题及其解决办法

    摘要: 玩转fetch. 作者:wonyun 原文:fetch使用的常见问题及其解决办法 Fundebug经授权转载,版权归原作者所有. 首先声明一下,本文不是要讲解fetch的具体用法,不清楚的可以 ...

  4. 多任务学习Multi-task-learning MTL

    https://blog.csdn.net/chanbo8205/article/details/84170813 多任务学习(Multitask learning)是迁移学习算法的一种,迁移学习可理 ...

  5. 201871010123-吴丽丽《面向对象程序设计(Java)》第十五周学习总结

    201871010123-吴丽丽<面向对象程序设计(Java)>第十五周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ ...

  6. Android TextField : set focus + soft input programmatically

    Good sir, try this: edittext.setFocusableInTouchMode(true); edittext.requestFocus(); Im not sure, bu ...

  7. 没有重写接口方法,IDEA没有报错。

    今天在IDEA写拦截器的时候遇到点困惑,继承了HandlerInterceptor没有报错,我一直认为他会提醒,要重写方法.如下图 通过查资料,嗯,终于找到原因来,先来上HandlerIntercep ...

  8. Autofac注册组件详解

    注册概念:我们通过创建 ContainerBuilder 来注册 组件 并且告诉容器哪些 组件 暴露了哪些 服务.组件 可以通过 反射 创建; 通过提供现成的 实例创建; 或者通过 lambda 表达 ...

  9. 快速调用Android虚拟机

    由于使用Android studio开发flutter 每次打开软件都需要很长时间,所以我自己使用vscode进行开发,没有虚拟机也开发不了,于是就有了这篇博客的作用啦 在本地桌面新建一个xxx.ba ...

  10. Aliyun STS Java SDK示例

    package com.aliyun.oss.demo; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.exceptions.Cl ...