golang redis lua脚本 和 lua function

lua script

go 复制代码
package redisx

import (
	"github.com/redis/go-redis/v9"
)

var GetOrSet = redis.NewScript(`
	local res = redis.call('GET', KEYS[1])
	if res ~= nil then
		return res
	end
	redis.call('SET', KEYS[1], ARGV[1])
	return ARGV[1]
`)

func Script() {
	redisClient := redis.NewClient(。。。) // 此次内容省略
	ret, err := GetOrSet.Run(context.Background(), redisClient, []string{"123"}, "654").Result()
	if err != nil {
		panic(err)
	}
	res := ret.(string)
	fmt.Println(res)
}

lua function

Redis 7 新特性之 自定义Functions

Redis Functions(函数)是用于管理服务端执行代码的API。在Redis 7中出现,旨在取代之前版本的EVAL函数,是Redis 7新特性之一。

Eval 脚本的缺点

Redis 7之前的版本通过Eval执行脚本,该命令允许发送Lua脚本供服务器执行。Eval脚本的核心作用是在Redis中高效、原子地执行应用程序逻辑。通过Lua脚本可以组合不同数据类型、不同键值原子执行。

使用EVAL需要应用程序每次都发送整个脚本以供执行。由于这会导致网络和脚本编译开销,Redis以EVALSHA命令的形式提供了优化。通过首先调用SCRIPT LOAD以获取脚本的SHA1,应用程序可以在之后单独使用SHA1重复调用脚本。

按照架构设计,Redis只缓存加载的脚本。这意味着脚本缓存随时可能丢失,例如在调用script FLUSH之后、重新启动服务器之后或故障切换到副本时。如果缺少脚本,应用程序负责在运行时重新加载脚本。基本假设是脚本是应用程序的一部分,不由Redis服务器维护。

这种方法适用于许多轻量级脚本用例,但一旦应用程序变得复杂并更加依赖脚本,就会带来一些困难:

所有客户端应用程序实例都必须维护所有脚本的副本

在事务上下文中调用缓存脚本会增加由于缺少脚本而导致事务失败的可能性

SHA1 设计作用不大 原因是调试非常困难

EVAL促进了一种反模式,即客户端应用程序逐字渲染脚本,而不是调用KEYS和ARGV Lua API

脚本之间不能相互调用 重复代码优化也成为无稽之谈

Redis Functions 介绍

Redis Functions是从Lua脚本进化而来。Functions提供与Lua脚本相同的核心功能。Redis将 Functions函数作为数据库的一个组成部分进行管理,并通过数据持久性和复制确保其可用性。因为函数是数据库的一部分,因此在使用前声明,所以应用程序不需要在运行时加载它们,也不需要冒中止事务的风险。使用函数的应用程序只依赖于它们的API,而不依赖于数据库中嵌入的脚本逻辑。

Redis Functions的设计还试图模糊编程语言的界限。Lua是Redis目前唯一支持作为嵌入式执行引擎的语言解释器,其目的是简单易学。然而,选择Lua作为一种语言仍然给许多Redis用户带来了挑战。Redis Functions特性对实现的语言没有任何限定。作为函数定义的一部分的执行引擎负责运行它。理论上,引擎可以用任何语言执行函数,只要它遵守若干规则(例如终止执行函数的能力)。

与Lua脚本操作一样,函数的执行是原子的。函数的执行在其整个时间内阻止所有服务器活动,这与事务的语义类似。这些语义意味着脚本的所有效果要么尚未发生,要么已经发生。执行函数的阻塞语义始终适用于所有连接的客户端。因为运行一个函数会阻塞Redis服务器,所以函数应该快速完成执行,所以应该避免使用长时间运行的函数。

总结:Redis Functions 类似MYSQL中的存储过程、自定义函数;事先定义Functions的逻辑,存储在服务端,客户端要做的仅仅是调用函数即可

script.lua

lua 复制代码
#!lua name=LibName
-- 注意:这一句设置namespace一定要加,要不然redis加载lua脚本的时候会报错,ERR Missing library metadata,
local function GetOrSet(KEYS, ARGV)
    local key = KEYS[1]
    local value = ARGV[1]
    local ttl = ARGV[2]
    local result = redis.call('GET', key)
    if result  then
        value = result..value
    end
    redis.call('SET', key, value)
    redis.call('EXPIRE', key, ttl)
    return value
end

redis.register_function('GetOrSet', GetOrSet)

redisx.go

复制代码
fs, err := os.Open("./script.lua")
	if err != nil {
		return
	}
	defer func(fs *os.File) {
		err := fs.Close()
		if err != nil {
			panic(err)
		}
	}(fs)
	binData, err := io.ReadAll(fs)
	if err != nil {
		panic(err)
	}
	if len(binData) == 0 {
		panic("binData is empty")
	}

	redisClient := redis.NewClient(。。。) // 此次内容省略
	err = redisClient .FunctionLoadReplace(context.Background(), string(binData)).Err()
	if err != nil {
		panic(err)
	}

	ret, err := redisClient .FCall(context.Background(), "GetOrSet", []string{"123"}, "654", 20).Result()
	if err != nil {
		panic(err)
	}

	res := ret.(string)
	fmt.Println(res)
相关推荐
学传打活13 小时前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活6 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16814 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心16 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552722 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552722 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝23 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552724 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552724 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节