概述
Redis的事务,Go+lua
Redis 事务
Redis的基本事务(basic transaction)需要用到MULTI命令和EXEC命令,这种事务可以让一个客户端在不被其他客户端打断的情况下执行多个命令。
和关系数据库那种可以在执行的过程中进行回滚(rollback)的事务不同,在Redis里面,被MULTI命令和EXEC命令包围的所有命令会一个接一个地执行,直到所有命令都执行完毕为止。当一个事务执行完毕之后,Redis才会处理其他客户端的命令。
Redis事务在执行的中途遇到错误,不会回滚,而是继续执行后续命令;
- 还未执行exec就报错:如果事务中出现语法错误,则事务会成功回滚,整个事务中的命令都不会提交.
如下例子, 整个事务都被回滚
127.0.0.1:6379> get c
"13"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> get c
QUEUED
127.0.0.1:6379> set
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>
- 成功执行exec后才报错:如果事务中出现的不是语法错误,而是执行错误,不会触发回滚,该事务中仅有该错误命令不会提交,其他命令依旧会继续提交。
如下例子,c
这个值就成功设置了
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> get a
QUEUED
127.0.0.1:6379> set b 12 EX xx
QUEUED
127.0.0.1:6379> set c 13
QUEUED
127.0.0.1:6379> exec
1) (nil)
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get c
"13"
127.0.0.1:6379>
lua
- Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
- Lua 教程
为什么要使用redis 执行 lua
使用Lua脚本的好处
- 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。
- 原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
- lua脚本可以常驻在redis内存中,所以在使用的时候,可以直接拿来复用,也减少了代码量
例子
这里列举了两个客户端的例子,分别是 github.com/go-redis/redis/v8 和
github.com/gomodule/redigo/redis
github.com/go-redis/redis/v8
https://redis.uptrace.dev/guide/lua-scripting.html
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
var incrBy = redis.NewScript(`
local key = KEYS[1]
local change = ARGV[1]
local value = redis.call("GET", key)
if not value then
value = 0
end
value = value + change
redis.call("SET", key, value)
return value
`)
var sum = redis.NewScript(`
local key = KEYS[1]
local sum = redis.call("GET", key)
if not sum then
sum = 0
end
local num_arg = #ARGV
for i = 1, num_arg do
sum = sum + ARGV[i]
end
redis.call("SET", key, sum)
return sum
`)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: ":6379",
})
_ = rdb.FlushDB(ctx).Err()
fmt.Printf("# INCR BYn")
for _, change := range []int{1, 5, 0} {
num, err := incrBy.Run(ctx, rdb, []string{"my_counter"}, change).Int()
if err != nil {
panic(err)
}
fmt.Printf("incr by %d: %dn", change, num)
}
fmt.Printf("n# SUMn")
sumResult, err := sum.Run(ctx, rdb, []string{"my_sum"}, 1, 2, 3).Int()
if err != nil {
panic(err)
}
fmt.Printf("sum is: %dn", sumResult)
}
github.com/gomodule/redigo/redis
package main
import (
"fmt"
"time"
"github.com/gomodule/redigo/redis"
)
const(
limitLua = `
redis.call('set', KEYS[1], 0, 'EX', ARGV[1], 'NX')
local num = redis.call('incr',KEYS[1])
if num > tonumber(ARGV[2])
then
return -1
end
return 0
`
)
func init()(){
initRedis()
}
var redisCoon redis.Conn
//初始化redis连接,redis在本地docker中运行
func initRedis()(){
redisClient := &redis.Pool{
// 最大空闲链接
MaxIdle: 10,
// 最大激活链接
MaxActive: 10,
// 最大空闲链接等待时间
IdleTimeout: 5 * time.Second,
Dial: func() (redis.Conn, error) {
rc, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
return nil, err
}
return rc, nil
},
}
redisCoon = redisClient.Get()
}
func AddCode(){
lua := redis.NewScript(1, limitLua)
exp := 60 // 60s
for i:=0;i<10;i++ {
in, err := redis.Int(lua.Do(redisCoon, "aLimit", exp, 5))
fmt.Println(in,"--",err)
}
}
func main() {
AddCode()
}
最后
以上就是烂漫网络为你收集整理的Redis的事务,Go+luaRedis的事务,Go+lua的全部内容,希望文章能够帮你解决Redis的事务,Go+luaRedis的事务,Go+lua所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复