redis中的lua

前言

最近在看redis的lua,看了官网资料和网上一些文章,整理了lua的相关内容,希望对大家有帮助。

目录

0. redis中运行lua的流程的正常流程 1.redis中的lua概要信息     1.1 lua中调用redis命令     1.2 redis数据结构与lua数据结构对应关系     1.3 EVAL和EVALSHA     1.4 脚本缓存     1.5 脚本命令     1.6 其他约定         1.6.1 全局变量保护         1.6.2 Select 使用         1.6.3 redis中lua脚本内置的lib 2.lua的脚本复制     2.1 whole scripts replication(仅脚本复制)     2.2 script effects replication(脚本影响复制Redis 3.2以后支持)     2.3 lua脚本中的可选复制命令 3. redis中lua脚本的debug     3.1 lua脚本中记录日志     3.2 Lua debugger 简要说明( Redis 3.2 提供)         3.2.1 LDB简介         3.2.2 快速入门 4. lua脚本执行超时处理     4.1 设置超时时间     4.2 脚本执行超时后处理方式 5. RedisTemplate调用lua脚本示例 6. 参考资料

0. redis中运行lua的流程的正常流程

1.redis中的lua概要信息

1.1lua中调用redis命令

   在lua脚本中以2种方式调用redis的命令

lua中调用redis的方式

对异常处理的方式

redis.call

遇到异常将抛出lua error

redis.pcall

会将错误信息进行包装,以lua的table类型返回。

2个工具函数

    redis.error_reply() 返回一个仅包含err元素的table

    redis.status_reply()返回一个仅包含ok元素的table

1.2redis数据结构与lua数据结构对应关系

Redis数据结构

lua数据结构

integer

number

bulk

String

multi bulk

table

status

lua的table中有一个ok做对应

error

lua的table中有一个err做对应

Nil bulk, Nil multi bulk

lua的boolean的false

注意:

  • Lua boolean true 会变为Redis 中的integer 1.
  • Lua中的所有number类型的数据,均会变成redis中的integer,采用截取的方式。如果需要lua返回float类型,请使用string作为返回值。
  • Redis中没有对nil进行转换的简单方法,如果lua的table中的元素有nil,redis无法进行转换。

举例说明:

1.3.EVAL和EVALSHA 

redis中2个命令执行lua脚本,EVAL和EVALSHA 。   EVAL 命令要求每次都发送脚本,带宽占用大。   EVALSHA命令为了减少带宽占用,提高效率而出现   EVALSHA 基本与EVAL命令一致,但是第一个参数是lua脚本的sha1值。 如果redis没有该sha1值对应的脚本,会抛出异常。 注意:pipeline中的EVALSHA 需要注意在pipeline中,建议如下:

  • 使用EVAL,保证不会出现脚本不存在的情况
  • 如果一定要使用EVALSHA,请先判断脚本是否存在,调用 SCRIPT EXISTS判断,不存在使用 SCRIPT LOAD 在pipeline中开头时使用。

1.4脚本缓存

   redis会缓存执行过的脚本,如果1个redis实例执行EVAL命令成功,所有后续的EVALSHA命令也会成功。    lua脚本相对Redis的数据来说,相对较小,可以忽略其内存占用。    SCRIPT FLUSH可以将redis缓存的脚本都移除。    一个持久化的redis和一个持久化的redis的连接,可以保证lua脚本发送过一次后,始终存在于内存中。  后续的EVALSHA执行都会成功。

1.5脚本命令

SCRIPT FLUSH      唯一可以让redis刷新脚本缓存的命令,一般用于云环境或者测试脚本时。 SCRIPT EXISTS sha1 sha2 ... shaN     判断给的的SHA1对应的脚本是否存在,返回一个列表按顺序对应之前的sha1值,列表元素1表示存在,0表示不存在。 SCRIPT LOAD script      向redis服务器注册lua脚本,确保EVALSHA使用正常。 SCRIPT KILL       没有修改数据的情况下,可以用这个命令中断脚本运行。

1.6其他约定

1.6.1 全局变量保护

Redis的lua脚本不允许声明全局变量,防止lua脚本泄漏数据,并保证AOF和同步从服务器能够正确运行, 如果脚本需要维持状态,可以将状态写入redis中。 Lua脚本可以使用2个全局变量KEYS和ARGV,这两个全局变量用于接收传递的KEY和args。

1.6.2 Select 使用

Redis的2.8.12版本后,select命令仅仅影响当前的脚本执行。

1.6.3 redis中lua脚本内置的lib

base lib                       table lib                 string lib. math lib                      struct lib                cjson lib. cmsgpack lib               bitop lib                redis.sha1hex function. redis.breakpoint          redis.debug (用于debug)

2.lua的脚本复制

2.1whole scripts replication(仅脚本复制,默认方式)

redis会将lua脚本复制到从服务器和持久化AOF文件中,因为发送脚本比发送一堆命令更高效。这种模式成为whole scripts replication(仅脚本复制)。 该模式的缺点:

  • Lua不导出命令来访问系统时间或其他外部状态
  • RANDOMKEY, SRANDMEMBER, TIME这几个函数在修改数据的脚本中不能使用,只能用于只读数据的脚本中。

注意:       SINTER;SUNION;SDIFF        SMEMEBERS;HKEYS;HVALS        KEYS        这几个命令具有不确定性因为redis的存储是乱序的,但是redis实现了默认按字典排序,保证每次lua脚本访问一致。

2.2script effects replication(脚本影响复制Redis 3.2以后支持)

Redis将lua脚本中的对数据的变更记录后,生成MULTI / EXEC 的事务发送到从服务器和AOF文件中。 使用redis.replicate_commands() 进行开启,返回true,表示脚本影响复制开启,否则表示未开启。  使用范围:

  •  脚本运行缓慢,但是脚本的修改较少。可以快速记录到从服务器和AOF文件中。
  • 当脚本影响复制开启后,非确定性脚本控制会关闭,可以随意使用     RANDOMKEY, TIME。

2.3lua脚本中的可选复制命令

该特性不推荐使用,具备一定风险 需要开启script effects replication,    redis.set_repl(redis.REPL_ALL) -- AOF和从服务器.    redis.set_repl(redis.REPL_AOF) -- 仅AOF.    redis.set_repl(redis.REPL_SLAVE) --仅从服务器.    redis.set_repl(redis.REPL_NONE) -- 不进行任何复制。

3. redis中lua脚本的debug

3.1lua脚本中记录日志

redis.log(loglevel,message)

loglevel 如下:

  • redis.LOG_DEBUG
  • redis.LOG_VERBOSE
  • redis.LOG_NOTICE
  • redis.LOG_WARNING

message仅仅接收String类型

举例:

redis.log(redis.LOG_WARNING,"Something is wrong with this script.")

3.2 Lua debugger 简要说明( Redis 3.2 提供)

3.2.1LDB简介

  • LDB使用的是 server-client模式,所以它是一个远程调试器.
  • Redis server 作为一个调试服务器,默认调试client端是redis-cli,其他client端可以根据redis的协议进行扩展。
  • 默认情况下,每一个调试进程都是一个单独的进程。这意味着在调试一个Lua脚本的同时,Redis不会阻塞,可以进行开发或者并行调试其他脚本。这也意味着调试进程中的所有更改均会回退(roll back),这保证使用同一份数据多次调试lua脚本不会存在问题。
  • redis也提供了同步模式,该模式下产生的变化将会保留,并会阻塞其他请求。

3.2.3快速入门

注意: 请不要再生环境产的Redis上进行lua脚本调试,请使用开发环境的Redis进行调试。注意同步调试(非默认)会阻塞所有的请求,可以使用redis.breakpoint()的方式动态设置断点。

异步调试方式:

./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2

同步模式:

./redis-cli --ldb-sync-mode --eval /tmp/script.lua

会接手如下3个参数

  • quit --停止debug,返回redis-cli
  • restart --调试进程重启,重新加载被调试的脚步
  • help --显示帮助信息

默认情况下是步进模式。

help显示列表如下

调试命令[缩写]

注释

[h]elp

显示这个列表

[s]tep

一步一步的进行调试,步进模式

[n]ext

下一步.

[c]continue

跳转到下一个断点

[l]list

显示当前行

[l]list [line]

显示第几行代码.line = 0 显示当前行.

[l]list [line] [ctx]

显示当前行,上下指定[ctx]的行数的代码

[w]hole

显示所有代码.

[p]rint

打印所有局部变量.

[p]rint <var>

打印指定变量,也可打印全局的KEYS和ARGV.

[b]reak

显示所有断点.

[b]reak <line>

在指定行添加断点.

[b]reak -<line>

移除指定行的断点.

[b]reak 0

移除所有断点.

[t]race

显示调用栈.

[e]eval <code>

执行一些Lua的代码(在不同的调用框架中).

[r]edis <cmd>

执行一个redis命令.

[m]axlen [len]

设置记录Redis响应与Lua变量dumps 的长度设置为0表示没有限制

[a]abort

停止脚步.同步模式下改变的数据将保留

debug示例:

我自己写了一个redislock的lua脚本,仅仅为了进行调试。

1.进入redis的src目录后执行如下语句,这里我们采用默认的调试方式(可以运行的前提是,redis版本3.2+,/usr/luascript中存在lock.lua脚本)

./redis-cli --ldb --eval /usr/luascript/lock.lua

会出现如下图的信息:

2.之后 我们输入whole或w线上脚本的所有语句

这个脚本比较简单就4句话,

3.之后我们在 第一行和第三行加入断点,数据b 1 3,之后入下图所示行号前加入了#号表示有断点

4.之后我们运行到下一个断点,输入c

这里显示参数必须是strings或者integers,实际上是我之前5.调试脚本的时候没有输入参数操作的,没有参数是KEYS和ARGV全局变量的值是nil,所以会报错误。

5. 我们退出调试进程,调试的语句为:

./redis-cli --ldb --eval /usr/luascript/lock.lua WWW , 12 1

注意 ,前后有空格,并重复2,3步后,

6.进行跳转到下一个断点,输入c

7.在这里我们看下KEYS和ARGV的值,输入p KEYS 和p ARGV

8.之后重复按c直到最后一步,会显示运行结果

4.lua脚本超时处理

4.1设置超时时间

在redis.conf中设置lua-time-limit 参数来自定义lua脚本的超时时间,单位是毫秒,默认是5000ms,不建议修改改值,目前5s的默认值已经非常大了,理论上一个lua脚本的执行时间应该是毫秒级别的。

4.2脚本执行超时后处理方式

当一个脚本超过时间现在,redis不会终止lua脚本,会进行如下操作:

  • 日志中记录该脚本执行时间过长
  • 对数据进行更改的lua脚本仅能被SHUTDOWN NOSAVE命令处理,该命令会丢失最近一次持久化后的数据。
  • 没有对数据进行更改的lua脚本可以被 SCRIPT KILL处理。
  • 开始接受其他命令,但是对于普通命令均会返回一个busy的error,仅仅会处理 SCRIPT KILL and SHUTDOWN NOSAVE 命令。

5.RedisTemplate如何调用lua脚本示例

1.先将脚本写入XXX.lua文件中,

2.之后将XXX.lua文件放入src/main/resources/lua中,如下图

RedisTemplate调用execute方法,第一个参数是脚本对象,第二个参数是个列表对应乱脚本中的KEYS,之后的可变参数对应lua脚本中的ARGV。具体操作如下:

@Service
public class LuaService {
	@Autowired
	StringRedisTemplate template;
	
	public void runLua() throws IOException{
		  //1,创建默认脚本对象
		  DefaultRedisScript<Boolean> script=new DefaultRedisScript<Boolean>();
		  //2.设置默认脚本数据源
		  ScriptSource scriptSource = new ResourceScriptSource(
                               new ClassPathResource("/lua/lock1.lua"));
		  script.setScriptSource(scriptSource);
		  //3.设置Lua脚本中的KEYS对象
		  List<String>keys=new ArrayList<String>();
			keys.add("WWWW");
		 template.execute(script, keys, "1200","1");
	}
	
}

6.参考资料

https://redis.io/commands/eval 官方关于redis的lua的数据

https://redis.io/topics/ldb 官方关于ldb调试的说明

http://wiki.jikexueyuan.com/project/redis/lua.html 极客学院的lua介绍

http://www.importnew.com/24124.html importnew关于lua的介绍

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring源码学习(三)炒鸡丁与populateBean没区别

    温安适
  • 基于zookeeper+leveldb的activemq集群

    温安适
  • 从零开始写简易读写分离,不难嘛!

    温安适
  • 《Redis设计与实现》读书笔记(三十四) ——Redis Lua脚本环境设计与实现

    《Redis设计与实现》读书笔记(三十四) ——Redis Lua脚本环境设计与实现 (原创内容,转载请注明来源,谢谢) 一、创建lua环境 为了在redis...

    用户1327360
  • lua脚本操作redis数据库

    为什么要用lua脚本操作redis数据库? 1.减少开销–减少向redis服务器的请求次数 2.原子操作–redis将lua脚本作为一个原子执行 3.可复...

    编程珠玑
  • 如何优雅地在Redis中使用Lua

    今天讲一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当中,来扩展其功能。lua脚本是用C语言写...

    黄泽杰
  • PixelBender(着色器)初体验

    只要是玩过photoshop的人,一定会对ps中的各式各样、功能强大的滤镜(filter)留下深刻的印象。 Adobe是靠图形处理软件起家的,这方面一直是它的强...

    菩提树下的杨过
  • docker搭建redis集群

    运行redis镜像 首先使用docker启动3个redis容器服务,分别使用到6379、6380、6381端口 docker run --name redis-...

    似水的流年
  • 3层交换技术

    我们知道不同的vlan之间要相互的通行必须要借用第三层,也就是网络层,可以路由器也可以是三层的交换机。三层拓扑图

    py3study
  • 理解unittest测试框架(五)——加载模块

    前面一系列文章研究了unittest框架的一些最小单元,比如用例,结果,这次看的是加载模块,也就是测试用例,是如何被框架加载到的。

    点点寒彬

扫码关注云+社区

领取腾讯云代金券