Redis的事务,Go+lua
Redis 事务
Redis的基本事务(basic transaction)需要用到MULTI命令和EXEC命令,这种事务可以让一个客户端在不被其他客户端打断的情况下执行多个命令。
和关系数据库那种可以在执行的过程中进行回滚(rollback)的事务不同,在Redis里面,被MULTI命令和EXEC命令包围的所有命令会一个接一个地执行,直到所有命令都执行完毕为止。当一个事务执行完毕之后,Redis才会处理其他客户端的命令。
Redis事务在执行的中途遇到错误,不会回滚,而是继续执行后续命令;
- 还未执行exec就报错:如果事务中出现语法错误,则事务会成功回滚,整个事务中的命令都不会提交.
如下例子, 整个事务都被回滚
复制代码
1
2
3
4
5
6
7
8
9
10
11
12127.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
这个值就成功设置了
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16127.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
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52package 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
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52package 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内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复