Redis Lua脚本调试
从版本3.2开始,Redis包含一个完整的Lua调试器,可以用来使编写复杂Redis脚本的任务更加简单。
由于Redis 3.2仍处于测试阶段,请unstable
从Github 下载Redis 的分支并编译它以测试调试器。您可以使用Redis unstable来调试稍后在稳定版本的Redis中运行的脚本,因此调试器已经可用于实际术语。
Redis Lua调试器,代号LDB,具有以下重要功能:
- 它使用服务器 - 客户端模型,因此它是一个远程调试器。Redis服务器充当调试服务器,而默认客户端则是
redis-cli
。但是,可以通过遵循服务器实现的简单协议来开发其他客户端。 - 默认情况下,每个新的调试会话都是分叉会话。这意味着在调试Redis Lua脚本时,服务器不会阻塞并可用于开发或者为了并行执行多个调试会话。这也意味着在脚本调试会话结束后回滚更改,因此可以使用与上一个调试会话完全相同的Redis数据集再次重新启动新的调试会话。
- 可以根据需要使用备用同步(非分叉)调试模型,以便可以保留对数据集的更改。在此模式下,服务器会阻止调试会话处于活动状态。
- 支持逐步执行。
- 支持静态和动态断点。
- 支持将调试脚本记录到调试器控制台中。
- 检查Lua变量。
- 跟踪由脚本执行的Redis命令。
- Redis和Lua值的漂亮印刷。
- 无限循环和长执行检测,模拟断点。
快速开始
开始使用Lua调试器的一个简单方法是观看此视频介绍:
重要说明:请确保使用Redis生产服务器避免调试Lua脚本。请改用开发服务器。另请注意,使用同步调试模式(这不是默认设置)会导致Redis服务器阻塞调试会话持续的所有时间。
要使用redis-cli
以下步骤启动新的调试会话:
- 使用首选编辑器在某个文件中创建脚本。假设您正在编辑位于的Redis Lua脚本
/tmp/script.lua
。 启动调试会话:
./redis-cli --ldb --eval /tmp/script.lua
请注意,通过--eval
选项,redis-cli
您可以将键名和参数传递给脚本,并以逗号分隔,如下例所示:
./redis-cli --ldb --eval /tmp/script.lua mykey somekey , arg1 arg2
您将进入一个redis-cli
不再接受其正常命令的特殊模式,而是打印一个帮助屏幕并将未修改的调试命令直接传递给Redis。
唯一没有传递给Redis调试器的命令是:
quit
- 这将终止调试会话。这就像删除所有断点并使用continue
debugging命令一样。此外,命令将退出redis-cli
。restart
- 调试会话将从头开始重新启动,从文件重新加载新版本的脚本。因此,正常的调试周期包括在调试之后修改脚本,并调用restart
以便在新脚本更改时再次开始调试。help
- 此命令传递给Redis Lua调试器,该调试器将打印如下命令列表:
lua debugger> help
Redis Lua debugger help:
[h]elp Show this help.
[s]tep Run current line and stop again.
[n]ext Alias for step.
[c]continue Run till next breakpoint.
[l]list List source code around current line.
[l]list [line] List source code around [line].
line = 0 means: current position.
[l]list [line] [ctx] In this form [ctx] specifies how many lines
to show before/after [line].
[w]hole List all source code. Alias for 'list 1 1000000'.
[p]rint Show all the local variables.
[p]rint <var> Show the value of the specified variable.
Can also show global vars KEYS and ARGV.
[b]reak Show all breakpoints.
[b]reak <line> Add a breakpoint to the specified line.
[b]reak -<line> Remove breakpoint from the specified line.
[b]reak 0 Remove all breakpoints.
[t]race Show a backtrace.
[e]eval <code> Execute some Lua code (in a different callframe).
[r]edis <cmd> Execute a Redis command.
[m]axlen [len] Trim logged Redis replies and Lua var dumps to len.
Specifying zero as <len> means unlimited.
[a]abort Stop the execution of the script. In sync
mode dataset changes will be retained.
Debugger functions you can call from Lua scripts:
redis.debug() Produce logs in the debugger console.
redis.breakpoint() Stop execution as if there was a breakpoint in the
next line of code.
请注意,启动调试器时,它将以步进模式启动。它将停止在脚本的第一行,它在执行之前实际执行某些操作。
从这一点开始,您通常会调用step
以执行该行并转到下一行。在您执行步骤时,Redis将显示服务器执行的所有命令,如以下示例所示:
* Stopped at 1, stop reason = step over
-> 1 redis.call('ping')
lua debugger> step
<redis> ping
<reply> "+PONG"
* Stopped at 2, stop reason = step over
的<redis>
和<reply>
线示出通过仅执行的行执行的命令,以及来自服务器的答复。请注意,这仅在步进模式下发生。如果您使用continue
以执行脚本直到下一个断点,则不会将命令转储到屏幕上以防止输出过多。
终止调试会话
当脚本自然终止时,调试会话结束并 redis-cli
以正常的非调试模式返回。您可以像往常一样使用该restart
命令重新启动会话。
停止调试会话的另一种方法是redis-cli
通过按下手动中断Ctrl+C
。请注意,也无论如何打破之间的连接redis-cli
和redis-server
将中断调试会话。
关闭服务器时,所有分叉的调试会话都将终止。
缩写调试命令
调试可能是一个非常重复的任务。因此,每个Redis调试器命令都以不同的字符开头,您可以使用单个初始字符来引用该命令。
因此,例如,step
您可以输入而不是键入s
。
断点
如在线帮助中所述,添加和删除断点很简单。只需使用b 1 2 3 4
在第1,2,3,4行添加b 0
断点。该命令将删除所有断点。可以使用as参数删除选定的断点,即我们要删除的断点所在的行,但前缀为减号。例如b -3
,从第3行中删除断点。
请注意,将断点添加到Lua从不执行的行(如声明局部变量或注释)将不起作用。断点将被添加,但由于脚本的这部分将永远不会被执行,程序将永远不会停止。
动态断点
使用该breakpoint
命令可以将断点添加到特定行中。但是,有时我们只想在发生特殊情况时才停止执行程序。为此,您可以使用redis.breakpoint()
Lua脚本中的 函数。调用时,它会模拟下一行中将要执行的断点。
if counter > 10 then redis.breakpoint() end
此功能在调试时非常有用,因此我们可以避免多次手动继续执行脚本,直到遇到给定条件。
同步模式
如前所述,但默认LDB使用分叉会话来回滚脚本在调试时所操作的所有数据更改。在调试期间,确定性通常是一件好事,因此可以启动连续的调试会话,而无需将数据库内容重置为其原始状态。
但是,为了跟踪某些错误,您可能希望保留每个调试会话对密钥空间执行的更改。当这是一个好主意,你应该使用一个特殊的选项,启动调试器ldb-sync-mode
中redis-cli
。
./redis-cli --ldb-sync-mode --eval /tmp/script.lua
请注意,在此模式下,在调试会话期间无法访问Redis服务器,因此请小心使用。
在此特殊模式下,该abort
命令可以使操作中的更改中途停止脚本到数据集。请注意,与正常结束调试会话相比,这是不同的。如果你只是中断redis-cli
脚本将完全执行,然后会话终止。相反,abort
您可以在中间中断脚本执行,并在需要时启动新的调试会话。
从脚本记录
该redis.debug()
命令是一个功能强大的调试工具,可以在Redis Lua脚本中调用,以便将内容记录到调试控制台中:
lua debugger> list
-> 1 local a = {1,2,3}
2 local b = false
3 redis.debug(a,b)
lua debugger> continue
<debug> line 3: {1; 2; 3}, false
如果脚本在调试会话之外执行,redis.debug()
则根本不起作用。请注意,该函数接受多个参数,这些参数由输出中的逗号和空格分隔。
正确显示表和嵌套表,以便为程序员调试脚本使值易于观察。
使用print
和检查程序状态eval
虽然redis.debug()
可以使用该函数直接从Lua脚本中打印值,但通常在步进或停止到断点时观察程序的局部变量很有用。
该print
命令就是这样,并在调用帧中执行查找,从当前的一个回到之前的一个,直到顶层。这意味着即使我们进入Lua脚本中的嵌套函数,我们仍然可以使用print foo
查看foo
调用函数的上下文中的值。在没有变量名称的情况下调用时,print
将打印所有变量及其各自的值。
该eval
命令在当前调用帧的上下文之外执行小块Lua脚本(使用当前Lua内部结构无法在当前调用帧的上下文中进行评估)。但是,您可以使用此命令来测试Lua函数。
lua debugger> e redis.sha1hex('foo')
<retval> "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
调试客户端
LDB使用客户端 - 服务器模型,其中Redis服务器充当使用RESP进行通信的调试服务器。虽然redis-cli
是默认的调试客户端,但只要满足以下条件之一,任何客户端都可用于调试:
- 客户端提供本机接口,用于设置调试模式和控制调试会话。
- 客户端提供了一个通过RESP发送任意命令的接口。
- 客户端允许将原始消息发送到Redis服务器。
例如,Redis的插件用于ZeroBrane工作室使用LDB集成Redis的-卢阿。以下Lua代码是插件如何实现的简化示例:
local redis = require 'redis'
-- add LDB's Continue command
redis.commands['ldbcontinue'] = redis.command('C')
-- script to be debugged
local script = [[
local x, y = tonumber(ARGV[1]), tonumber(ARGV[2])
local result = x * y
return result
]]
local client = redis.connect('127.0.0.1', 6379)
client:script("DEBUG", "YES")
print(unpack(client:eval(script, 0, 6, 9)))
client:ldbcontinue()
Redis Lua脚本调试的更多相关文章
- Redis Lua 脚本使用
本文转载自Redis Lua 脚本使用 Lua 简介 Lua语言提供了如下几种数据类型:booleans(布尔).numbers(数值).strings(字符串).tables(表格). 下面是一些 ...
- Redis Lua脚本完全入门
1. 前言 Redis是高性能的KV内存数据库,除了做缓存中间件的基本作用外还有很多用途,比如胖哥以前分享的Redis GEO地理位置信息计算.Redis提供了丰富的命令来供我们使用以实现一些计算.R ...
- windows redis+lua的调试
1.编写lua脚本my.lua local key = KEYS[1] --限流KEY(一秒一个) local limit = tonumber(ARGV[1]) --限流大小 local curre ...
- Redis Lua脚本原理
2.6版本之后支持嵌入Lua脚本,客户端使用Lua脚本,直接在服务器端原子的执行多条命令 Lua脚本执行过程 创建并修改Lua环境 1 创建基础Lua环境 2 载入函数库 3 创建全局表格Lua 4 ...
- 在Spring中使用Redis Lua脚本批量删除缓存
背景 之前分享了一篇利用lua脚本批量删除redis的key的文章.现在项目中我打算使用spring的缓存,而Spring缓存以前我是用ehcache来做实现的.没发现什么问题..这次我换成redis ...
- 【连载】redis库存操作,分布式锁的四种实现方式[四]--基于Redis lua脚本机制实现分布式锁
一.redis lua介绍 Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题.Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向 ...
- redis --- lua 脚本实现原子操作
如题, 楼主的想法很简单, lua 脚本本身支持原子性, 所以把命令写进一个脚本就行, 当然后续还会优化才能放到生产上,例如缓存脚本 ,redis 本身会缓存执行过的脚本 ,这样速度更快, 再优化, ...
- Redis进阶应用:Redis+Lua脚本实现复合操作
一.引言 Redis是高性能的key-value数据库,在很大程度克服了memcached这类key/value存储的不足,在部分场景下,是对关系数据库的良好补充.得益于超高性能和丰富的数据结构,Re ...
- redis lua脚本学习
语法格式(常见) a = 5 -- 全局变量 local b = 5 -- 局部变量 Eval的使用 EVAL script numkeys key [key ...] arg [arg ...] 首 ...
随机推荐
- NPOI导出Excel2007-xlsx
今天在用npoi导出xls时会报错,经过在网上查找资料,找到一篇博客文章介绍的,原文地址https://www.cnblogs.com/spring_wang/p/3160020.html 1.今天再 ...
- Newtonsoft.Json 的基本用法
Ø 前言 说起 C# 对 JSON 的操作(序列化与反序列化),大家都会想到 JavaScriptSerializer.DataContractJsonSerializer 与 Newtonsoft ...
- Javaweb学习笔记——(二十一)——————过滤器
过滤器 过滤器概述 1.什么是过滤器: 过滤器javaweb三大组件之一,它与Serlvet很相似,不过它过滤器是用来拦截请求的,而不是处理 ...
- Tomcat连接池配置与实现/JNDI
方法一: 在Tomcat的conf/context.xml中配置在Tomcat\apache-tomcat-6.0.33\conf目录下的context.xml文件中配置默认值如下: <?xml ...
- 关于PHP中的全局变量global和$GLOBALS的不同区分
1.global Global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件. 但是在函数体内定义的global变量,函数体 ...
- 基于TensorFlow Object Detection API进行相关开发的步骤
*以下二/三.四步骤确保你当前的文件目录是以research文件夹为相对目录. 一/安装或升级protoc 查看protoc版本命令: protoc --version 如果发现版本低于2.6.0或运 ...
- JavaScript中Float类型保留两位小数
JavaScript中Float类型保留两位小数 核心方法: num:要操作的数字 size:要保留的位数 parseFloat(num).toFixed(size); 实现代码如下:var ...
- Element Tabs 组件中使用 ve-histogram组件渲染不出来(已解决)
Element Tabs 组件中使用 ve-histogram组件渲染不出来 发现问题提了issue,饿了么前端“西瓜”同学很快做了回复,饿了么大前端团队有沉淀很专业,赞. tip: GitHub 的 ...
- $(function() {....}) ,(function($){...})(jQuery)
$(function() {....}) 是 jQuery 中的经典用法,等同于 $(document).ready(function() {....}),即在页面加载完成后才执行某个函数,如果函数中 ...
- CSS3里的瀑布流效果
页面伸缩之后,内部的布局会自动缩放,高度不同的内容也会自动以垂直瀑布的效果平铺. 下面就是一部分代码: CSS: /*大层*/ .container{width:%;margin: auto;} /* ...