前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解 Redis Lua 脚本调试技巧和最佳实践

深入理解 Redis Lua 脚本调试技巧和最佳实践

作者头像
Tinywan
发布2024-01-10 15:15:51
3490
发布2024-01-10 15:15:51
举报
文章被收录于专栏:开源技术小栈开源技术小栈

Redis Lua 脚本调试是一种强大的工具,可以帮助您快速发现和解决Lua脚本中的问题。它允许您在运行脚本时逐步执行脚本,并检查每个步骤的结果。

两种调试模式

从Redis 3.2开始,内置了 Lua debugger(简称LDB),使用Lua debugger可以很方便的对我们编写的Lua脚本进行调试

异步模式 --ldb

开启 lua dubegger ,将会进入debug命令行。这个模式下 redis 会 fork 一个进程进入隔离环境,不会影响 redis 正常提供服务,但调试期间,原始 redis 执行命令、脚本的结果也不会体现到 fork 之后的隔离环境之中

同步模式 --ldb-sync-mode

同步模式,这个模式下,会阻塞 redis 上所有的命令、脚本,直到脚本退出,完全模拟了正式环境使用时候的情况,使用的时候务必注意这点。

参考案例

代码语言:javascript
复制
/data/lua # redis-cli -a 123456 --ldb --eval /data/lua/pong.lua
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
Lua debugging session started, please use:
quit    -- End the session.
restart -- Restart the script in debug mode again.
help    -- Show Lua script debugging commands.

* Stopped at 1, stop reason = step over
-> 1   local foo = redis.call('ping') 
lua debugger>
  • -a 123456 Redis 登录密码
  • --ldb 异步模式
  • --eval 运行一个脚本
  • help 可以查看更多帮助信息

Lua 脚本

案例1、执行一条命令

pong.lua 脚本

代码语言:javascript
复制
local foo = redis.call('ping')
return foo

运行截图

案例2、携带参数的脚本执行

demo1.lua

代码语言:javascript
复制
local src = KEYS[1]
local dst = KEYS[2]
local count = tonumber(ARGV[1])
return true

运行截图

打印所有的KEYS

代码语言:javascript
复制
lua debugger> print KEYS
<value> {"list_a"; "list_b"; "10"}

打印所有的ARGV

代码语言:javascript
复制
lua debugger> print ARGV
<value> {"10"}
案例3、脚本中执行 Redis 命令

demo2.lua

代码语言:javascript
复制
local src = KEYS[1]
local dst = KEYS[2]
local count = tonumber(ARGV[1])

-- 移除 list_a 列表的最后一个元素,返回值为移除的元素,即:e
local item = redis.call('rpop', src)
-- 将 e 值插入到 list_b 列表表头部
redis.call('lpush', dst, item)
-- 返回 list_b 列表长度
return redis.call('llen', dst)

准备数据

执行结果

注意:KEYSARGV 使用 , 逗号分隔

  • r 调试命令可以 执行redis命令,在调试环境里
  • redis.call("redis command") 函数执行Redis 命令
  • print 可以打印脚本中的变量
案例4、脚本中执行 Redis 命令

demo3.lua

代码语言:javascript
复制
local src = KEYS[1]
local dst = KEYS[2]
local count = tonumber(ARGV[1])

while count > 0 do
    local item = redis.call('rpop', src)
    redis.debug("value of item: ",item);
    if item ~= false then
        redis.call('lpush', dst, item)
    end
   count = count - 1    
end
return redis.call('llen', dst)
  • redis.debug() 函数打印变量信息,配置 c 调试命令,可以一次性输出所有值
  • restart 修改lua脚本后,执行该命令可以重新开始调试
  • quit可以退出调试模式

如果移除代码 count = count - 1 ,则会进入系循环

案例5、多个命令执行

demo4.lua

代码语言:javascript
复制
local src = KEYS[1]
local dst = KEYS[2]
local count = tonumber(ARGV[1])

while count > 0 do
    local username = redis.call('get',src)
    redis.debug('username : ',username)
    local age = redis.call('get',dst)
    redis.debug('age : ',age)
    count = count - 1
end
  1. 使用 w(whole) 命令,显示所有代码,看看需要在哪一行打断点

  1. 例如要在第7行打断点,则需要输入 b 7
代码语言:javascript
复制
lua debugger> b 7
   6       local username = redis.call('get',src) 
  #7       redis.debug('username : ',username) 
   8       local age = redis.call('get',dst) 

  1. 查看所有断点,输入命令:b

  1. 我们需要直接运行到打断点的地方,则需要输入:c 命令,会直接跳转到打第一个断点的语句,这时候可以打印断点之前的 变量,以下可以看出运行结果:

  1. 命令 b 0 删除所有断点(这里删除后,再第9行我们在打个断点),再次输入 b,发现已经没有断点。并且断点后面的值没法打印,只能打断点之前的变量

  1. 第 9 行打断点(b 9 ),查看所有代码,第9行已经被打伤断点了

  1. 再次跳转(c 命令)到打断点的地方。再次打印变量,发现已经可以打印了

  1. print 打印所有变量
案例6、Redis lua 版本和函数

Redis Lua脚本是 5.1.5

代码语言:javascript
复制
-- Copyright (C) ShaoBo Wan (Tinywan)

local KEYS1 = KEYS[1]
local KEYS2 = KEYS[2]
local ARGV1 = ARGV[1]
local ARGV2 = ARGV[2]
local ARGV3 = ARGV[3]
local ARGV4 = ARGV[4]
local status, type = next(redis.call('TYPE', KEYS[1])) -- type=none status=ok 
if status ~= nil and status == 'ok' then
    if type == 'zset' then
        -- list = {"10090"; "10089"; "10088"; "10087"; "10086"}
        local list = redis.call('ZREVRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'LIMIT', ARGV[3], ARGV[4])
        -- 获取数组table长度:#list
        local kk = #list
        -- unpack它接受一个数组(table)作为参数,并默认从下标1开始返回数组的所有元素
        local k1, k2, k3, k4 ,k5 = unpack(list)
        redis.debug('k1 ', k1) -- 10090
        redis.debug('k2 ', k2) -- 10089
        redis.debug('k3 ', k3) -- 10088
        redis.debug('k4 ', k4) -- 10087
        redis.debug('k5 ', k5) -- 10087
        if list ~= nil and #list > 0 then
            -- ZREM key member [member ...]
            redis.call('ZREM', KEYS[1], unpack(list)) -- unpack(list) 返回过期数组的所有元素
            -- HMGET key field [field ...]
            local result = redis.call('HMGET', KEYS[2], unpack(list)) -- ["username:Tinywan,age:24"]
            -- HDEL key field [field ...]
            redis.call('HDEL', KEYS[2], unpack(list)) -- 1
            return result
        end
    end
end
return nil

  • next() 函数:第一个值返回函数是否执行成功(ok),第二个值返回执行结果(对应的值)>
    • 如果该key不存在,则返回none
    • 如果该key存在,则返回该key数据结构类型,如上返回 zset,表示有序集合。
  • unpack() 函数:unpack它接受一个数组(table)作为参数,并默认从下标1开始返回数组的所有元素
  • 移除有序集中的一个或多个成员 ZREM key member [member ...]
  • 获取多个字段的hash值数组HMGET key field [field ...]
  • 删除hash值的key HDEL key field [field ...]
  • b 27 ,b 28 打两个端点
  • c 命令直接到第一个端点(b 26)
  • p 打印之前所有的变量

重要: 以上消费者脚本会直接删除有序集合key和所对应的哈希值。所以为了消息的可靠性。通过以上脚本返回的值会存储在一个stream流中,如果在stream消费失败(没有进行ACK机制),则会进入待办Pending队列重复消费(知道ACK机制或者删除该消息队列)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 两种调试模式
  • Lua 脚本
    • 案例1、执行一条命令
      • 案例2、携带参数的脚本执行
        • 案例3、脚本中执行 Redis 命令
          • 案例4、脚本中执行 Redis 命令
            • 案例5、多个命令执行
              • 案例6、Redis lua 版本和函数
              相关产品与服务
              云数据库 Redis
              腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档