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命令

相关推荐
趣味编程1112 小时前
go的学习2---》并发编程
学习·golang·perl
虫师c6 小时前
分布式缓存实战:Redis集群与性能优化
redis·分布式·缓存·redis集群·高可用架构·生产环境·数据分片
我真的是大笨蛋18 小时前
Redis的String详解
java·数据库·spring boot·redis·spring·缓存
爱好学习的青年人20 小时前
一文详解Go语言字符串
开发语言·后端·golang
zhengzizhe20 小时前
Redssion出现attempt to unlock lock, not locked by current thread by node id
redis
兜兜风d'1 天前
redis字符串命令
数据库·redis·缓存
思考的笛卡尔1 天前
Go语言实战:高并发服务器设计与实现
服务器·开发语言·golang
西瓜er1 天前
Docker 一键部署指南:GitLab、Nacos、Redis、MySQL 与 MinIO 全解析
redis·docker·gitlab
道可到1 天前
别再瞎拼技术栈!Postgres 已经能干 Redis 的活了
redis·后端·postgresql