在上一篇文章中,我们探讨了 Redis 的 Pipeline 操作及其在高效处理大量命令时的应用。今天,我们将深入了解 Redis 的脚本功能,包括如何在 Go 语言中使用 go-redis 库执行脚本。我们将介绍 Redis 的脚本功能、eval 和 function 操作的常见场景,并详细讲解 go-redis 中相关的方法及示例代码。
在《go-redis 使用指南》系列文章中,我们将详细介绍如何在 Golang 项目中使用 redis/go-redis 库与 Redis 进行交互。以下是该系列文章的全部内容:
Redis 提供了强大的脚本功能,允许用户使用 Lua 脚本在 Redis 服务器端执行复杂的操作。这些脚本可以大大简化客户端与 Redis 之间的数据交换,并提升操作效率。Redis 的脚本功能主要包括两大类:eval 和 function。
以下是 go-redis 提供的与 script 相关的操作方法:
package main
import (
"context"
"fmt"
"log"
"github.com/redis/go-redis/v9"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6380",
})
// 清除所有脚本缓存
if _, err := rdb.ScriptFlush(ctx).Result(); err != nil {
log.Fatalf("Error flushing scripts: %v", err)
}
// 设置初始键值
rdb.Set(ctx, "key1", 10, 0)
rdb.Set(ctx, "key2", 20, 0)
// Lua 脚本,计算两个键值的和并返回
script := `
local val1 = redis.call("GET", KEYS[1])
local val2 = redis.call("GET", KEYS[2])
val1 = tonumber(val1) or 0
val2 = tonumber(val2) or 0
return val1 + val2
`
keys := []string{"key1", "key2"}
args := []interface{}{}
// 将脚本加载到 Redis 并获取 SHA1 值
sha1, err := rdb.ScriptLoad(ctx, script).Result()
if err != nil {
log.Fatalf("Error loading script: %v", err)
}
fmt.Printf("Loaded script SHA1: %s\n", sha1)
// 检查脚本是否已经存在
exists, err := rdb.ScriptExists(ctx, sha1).Result()
if err != nil || !exists[0] {
log.Fatalf("Script does not exist: %v", err)
}
fmt.Println("Script exists in Redis")
// 执行脚本
result, err := rdb.Eval(ctx, script, keys, args...).Result()
if err != nil {
log.Fatalf("Error executing script: %v", err)
}
fmt.Printf("Result of Eval: %v\n", result)
// 根据 SHA1 值执行脚本
resultSha, err := rdb.EvalSha(ctx, sha1, keys, args...).Result()
if err != nil {
log.Fatalf("Error executing script by SHA1: %v", err)
}
fmt.Printf("Result of EvalSha: %v\n", resultSha)
// 执行只读的 Lua 脚本 (需要 Redis 7.0 以上)
resultRO, err := rdb.EvalRO(ctx, script, keys, args...).Result()
if err != nil {
log.Fatalf("Error executing read-only script: %v", err)
}
fmt.Printf("Result of EvalRO: %v\n", resultRO)
// 根据 SHA1 值执行只读的 Lua 脚本 (需要 Redis 7.0 以上)
resultShaRO, err := rdb.EvalShaRO(ctx, sha1, keys, args...).Result()
if err != nil {
log.Fatalf("Error executing read-only script by SHA1: %v", err)
}
fmt.Printf("Result of EvalShaRO: %v\n", resultShaRO)
// 杀死当前正在执行的脚本(若有)
if err := rdb.ScriptKill(ctx).Err(); err != nil && err != redis.Nil {
log.Fatalf("Error killing script: %v", err)
}
fmt.Println("Killed running script (if any)")
}
执行代码,输出:
Loaded script SHA1: 4f542ba45ede2410735a8a396120ade717f1a142
Script exists in Redis
Result of Eval: 30
Result of EvalSha: 30
Result of EvalRO: 30
Result of EvalShaRO: 30
2024/08/26 18:27:49 Error killing script: NOTBUSY No scripts in execution right now.
exit status 1
package main
import (
"context"
"fmt"
"log"
"github.com/redis/go-redis/v9"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6380",
})
// 清除所有已加载的 Redis Functions
if _, err := rdb.FunctionFlush(ctx).Result(); err != nil {
log.Fatalf("Error flushing functions: %v", err)
}
// 加载一个纯读取操作的 Redis Function
functionCode := `#!lua name=mylib
redis.register_function('get_sum', function(keys, args)
local sum = 0
for i, key in ipairs(keys) do
sum = sum + tonumber(redis.call("GET", key))
end
return sum
end)
`
if _, err := rdb.FunctionLoad(ctx, functionCode).Result(); err != nil {
log.Fatalf("Error loading function: %v", err)
}
fmt.Println("Function 'get_sum' loaded successfully")
// 列出所有已加载的 Redis Functions
functions, err := rdb.FunctionList(ctx, redis.FunctionListQuery{}).Result()
if err != nil {
log.Fatalf("Error listing functions: %v", err)
}
fmt.Printf("Loaded functions: %v\n", functions)
// 执行加载的 Redis Function
result, err := rdb.FCall(ctx, "get_sum", []string{"key1", "key2"}).Result()
if err != nil {
log.Fatalf("Error executing function: %v", err)
}
fmt.Printf("Result of FCall 'get_sum': %v\n", result)
// 替换已存在的 Redis Function
newFunctionCode := `#!lua name=mylib
redis.register_function('get_sum', function(keys, args)
return "xxx"
end)
`
if _, err := rdb.FunctionLoadReplace(ctx, newFunctionCode).Result(); err != nil {
log.Fatalf("Error replacing function: %v", err)
}
fmt.Println("Function 'get_sum' replaced successfully")
// 导出 Redis Functions
dump, err := rdb.FunctionDump(ctx).Result()
if err != nil {
log.Fatalf("Error dumping functions: %v", err)
}
fmt.Println("Functions dumped successfully")
// 删除 Redis Function
if _, err := rdb.FunctionDelete(ctx, "mylib").Result(); err != nil {
log.Fatalf("Error deleting function: %v", err)
}
fmt.Println("Function 'get_sum' deleted successfully")
// 异步清除 Redis Functions
if _, err := rdb.FunctionFlushAsync(ctx).Result(); err != nil {
log.Fatalf("Error flushing functions asynchronously: %v", err)
}
fmt.Println("Functions flushed asynchronously")
// 恢复导出的 Redis Functions
if _, err := rdb.FunctionRestore(ctx, dump).Result(); err != nil {
log.Fatalf("Error restoring functions: %v", err)
}
fmt.Println("Functions restored successfully")
// 获取 Redis Functions 的统计信息
stats, err := rdb.FunctionStats(ctx).Result()
if err != nil {
log.Fatalf("Error getting function stats: %v", err)
}
fmt.Printf("Function stats: %v\n", stats)
// 杀死当前正在执行的 Function(若有)
if err := rdb.FunctionKill(ctx).Err(); err != nil && err != redis.Nil {
log.Fatalf("Error killing function: %v", err)
}
fmt.Println("Killed running function (if any)")
}
执行代码,输出结果:
Function 'get_sum' loaded successfully
Loaded functions: [{mylib LUA [{get_sum []}] }]
Result of FCall 'get_sum': 30
Function 'get_sum' replaced successfully
Functions dumped successfully
Function 'get_sum' deleted successfully
Functions flushed asynchronously
Functions restored successfully
Function stats: {[{LUA 1 1}] false { [] 0} []}
2024/08/26 20:46:27 Error killing function: NOTBUSY No scripts in execution right now.
exit status 1
本文详细介绍了在 Golang 中使用 go-redis 执行 Redis script 脚本的操作方法,并提供了相关示例代码。通过这些方法,你可以更加灵活高效地在 Redis 中执行复杂操作。
希望这篇文章能帮助你更好地理解和使用 go-redis,点击 go-redis 使用指南 可查看更多相关教程!