redis lua脚本(go)调用教程以及debug调试

一、GO调用核心函数

这里用go代码来展示调用,首先是核心函数介绍:

go 复制代码
func NewScript(src string) *redisv9.Script {
func (s *Script) Load(ctx context.Context, c Scripter) *StringCmd {
func (c cmdable) EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd {

第一个函数传入 lua脚本字符串,然后生成 redisv9.Script 对象

第二个函数 是通过redisv9.Script 对象的load函数 将对象存的 lua脚本字符串,传到 redis节点, 同时返回 sha,和error

第三个 redis.EvalSha 通过 sha值来确定 redis存的lua脚本,keys是传入的key值, args是参数。

keys再lua代码里是 KEYS, args在lua代码 是ARGS
直接上代码:

go 复制代码
func TestRedisLua(t *testing.T) {
	InitRedis()		// 注意这个是我本地写的链接redis的函数,读者需要自己写(这里的 db.RedisCon2 就是 *redis.Client)
	// 这个是要执行的lua脚本
	luaScript = `return "keys="..KEYS[1] .. "|" ..KEYS[2].. "\nargs=" ..ARGV[1].. "|" ..ARGV[2]`

	redisScript := redis.NewScript(luaScript)	// 初始化脚本
	// 将脚本传入reids节点
	sha, err := redisScript.Load(context.TODO(), db.RedisCon2.GetClient()).Result()
	if err != nil {
		appzaplog.Error("redis load err", zap.Error(err))
		return
	}
	ret, retErr := db.RedisCon2.GetClient().EvalSha(context.Background(), sha, []string{
		"v1:confession_twall",
		"v1:confession_pwall:rec:1000001",
	}, 1758273500, "3331").Result()
	if retErr != nil {
		appzaplog.Error("redis eval err", zap.Error(retErr))
		return
	}
	fmt.Println(ret.(string))
end

执行结果

干货:

  1. **测试用法 :**测试的时候这三个函数其实也可以用一个函数替代:
    直接传入 lua脚本字符串,key,和参数即可
go 复制代码
func (c cmdable) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd {
  1. 正式用法 NewScript 和 Load的作用是为了缓存对象,在频繁调用的时候可以显著减少 因构建对象的产生的消耗;NewScript 是go对象构建, Load是redis里的lua对象构建。

正常代码写法(伪代码)

go 复制代码
var Sha1 = ""
// 判断 sha值是否过期
ret, err := db.RedisCon2.GetClient().ScriptExists(context.TODO(), Sha1 ).Result()
if err != nil{
...
}
if ret == false{	// sha值过期了,重新Load(redisScript 之前初始化过后,不用二次初始化)
	Sha1 , err = redisScript.Load(context.TODO(), db.RedisCon2.GetClient()).Result()
	if err != nil {
		appzaplog.Error("redis load err", zap.Error(err))
		return
	}
}
// 执行EvalSha操作

二、高级技巧:debug调试(linux端)

1.调试命令

go 复制代码
 redis-cli --ldb-sync-mode  --eval  lua脚本路径  key1 key2 , 参数1 参数2
 redis-cli --ldb  --eval  lua脚本路径  key1 key2 , 参数1 参数2

1.1坑点讲解

  1. redis-cli --ldb-sync-mode 是阻塞的 可以断点调试,但是生产环境不要用,内网也别用(可能会被骂),最好用本地的
  2. redis-cli --ldb 不阻塞,但是这个不能断点
  3. 这个点比较离谱 逗号(,)用来区分可变参数 KEYS 和 ARGV的,但是这个逗号前后一定要空格,不要识别不了

2. 示例:
2.1构造数据

构造完成后 含 Content ReqTime的数据在最左边

bash 复制代码
redis-cli 		# 进入redis,插入数据
lpush KEYAAA 111 222 333
lpush KEYBBB 111 222 333
lpush KEYAAA {"BlackFlag":0,"RecId":1000001,"SendId":1000013,"Content":"3331","ReqTime":1758273500}
lpush KEYBBB {"BlackFlag":0,"RecId":1000001,"SendId":1000013,"Content":"3332","ReqTime":1758273500}

2.1 编写lua代码:

代码大意:搜索两个KEY中LIST元素,满足ReqTime 和 Content 和参数一致,且没有被处理过的元素。搜索到后将其删除并在最右边生成(实现右移

lua 复制代码
local function DelInfo(delKey, reqTime, content)
    local len = redis.call('LLEN', delKey)
    for i = 0, len - 1 do
        local element = redis.call('LINDEX', delKey, i)
        local ok, data = pcall(cjson.decode, element)
        if ok and type(data) == 'table' then
            -- 条件判断
            if tostring(data.ReqTime) == reqTime and data.Content == content and data.BlackFlag == 0 then
                -- 设置 BlackFlag = 1
                data.BlackFlag = 1
                -- 重新序列化并更新该位置元素
                local new_element = cjson.encode(data)
                redis.call('LREM', delKey, 1, element)  -- 删除原数据 (json相等的字符串绝对包含》decode后三个字段满足的元素)
                redis.call('RPUSH', delKey, new_element)    -- 放入最右边
                return data.SendId
            end
        else
            return 6
        end
    end
    return nil
end

local function CallDel()
    local sendId = DelInfo(KEYS[1], ARGV[1], ARGV[2])
    if sendId == nil then
        return 1
    end
    sendId = DelInfo(KEYS[2], ARGV[1], ARGV[2])
    if sendId == nil then
        return 2
    end
    return 0
end

return CallDel()

2.2 尝试执行

bash 复制代码
 redis-cli --ldb-sync-mode  --eval confess_lua.lua  KEYAAA  KEYBBB  ,  1758273500 3331
 c # 回车

正确的效果应该是:

含 Content ReqTime的数据在最左边 ,但发现KEYAAA 是正常的,但是KEYBBB没有变化(其实故意造错数据,方便用调试测试)
2.3 调试
先重新构建数据:(每次调试都执行一遍)

bash 复制代码
redis-cli 		# 进入redis,插入数据
del KEYAAA 
del KEYBBB
lpush KEYAAA 111 222 333
lpush KEYBBB 111 222 333
lpush KEYAAA {"BlackFlag":0,"RecId":1000001,"SendId":1000013,"Content":"3331","ReqTime":1758273500}
lpush KEYBBB {"BlackFlag":0,"RecId":1000001,"SendId":1000013,"Content":"3332","ReqTime":1758273500}

进入调试:

bash 复制代码
 redis-cli --ldb-sync-mode  --eval confess_lua.lua  KEYAAA  KEYBBB  ,  1758273500 3331
 # 下面输入 调试命令后自己加回车
 b 8
 c	   # 运行直到遇到断点
 p data	# 打印变量
 p reqTime
 p content	
# 最后发现 是KEYBBB 运行到 第八行的时候没有继续,打印变量发现是 content == 3331, data.Content == 3332 条件不满足

上面的调试命令应该是

加完断点输入运行(c+enter)后,遇到断点 打印 p data 和 p content,会发现第二次触发断点时打印数值发现content对不上:

原因是构建redis数据的时候

Content 应该是 3331 但是写成了 3332

所以你成功通过调试找到bug所在!!!

下面我们重新构建数据

go 复制代码
redis-cli 		# 进入redis,插入数据
del KEYAAA 
del KEYBBB
lpush KEYAAA 111 222 333
lpush KEYBBB 111 222 333
lpush KEYAAA {"BlackFlag":0,"RecId":1000001,"SendId":1000013,"Content":"3331","ReqTime":1758273500}
lpush KEYBBB {"BlackFlag":0,"RecId":1000001,"SendId":1000013,"Content":"3331","ReqTime":1758273500}```

然后执行不阻塞命令:

bash 复制代码
 redis-cli --ldb  --eval confess_lua.lua  KEYAAA  KEYBBB  ,  1758273500 3331

查看redis数据,就发现目标数据已经右移了

调试命令大全:

当你进入调试时,输入命令 help可以看到调试命令

常见调试命令:

s 单步执行 进入函数

n 单步执行 不进入函数

c 继续执行直到遇到断点

b 设置断点输入数字 指定某行,输入 函数指定函数开头,输入0清除所有

l 查看当前断点前后代码

p 打印

r 执行redis命令

相关推荐
l1t1 小时前
利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型
c语言·数据库·单元测试·lua·duckdb
睡前要喝豆奶粉1 小时前
在.NET Core Web Api中使用redis
redis·c#·.netcore
草明2 小时前
Go 的 IO 多路复用
开发语言·后端·golang
祈祷苍天赐我java之术3 小时前
如何在Java中整合Redis?
java·开发语言·redis
星梦清河5 小时前
Redis(四):缓存击穿及其解决方案(SpringBoot+mybatis-plus)
spring boot·redis·缓存
傻啦嘿哟5 小时前
用Redis实现爬虫URL去重与队列管理:从原理到实战的极简指南
数据库·redis·爬虫
Mr. zhihao8 小时前
Java 反序列化中的 boolean vs Boolean 陷阱:一个真实的 Bug 修复案例
java·bug·lua
@爱学习的小趴菜8 小时前
Redis服务器配置
服务器·数据库·redis
Maỿbe11 小时前
Redis的持久化
数据库·redis·缓存
ellis197011 小时前
Lua代码混淆-Prometheus方案教程
unity·lua