Redis的事务,Go+lua

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 BY\n")
	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: %d\n", change, num)
	}

	fmt.Printf("\n# SUM\n")
	sumResult, err := sum.Run(ctx, rdb, []string{"my_sum"}, 1, 2, 3).Int()
	if err != nil {
		panic(err)
	}
	fmt.Printf("sum is: %d\n", 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()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是一个使用Lua脚本实现Redis分布式锁的代码示例: ```lua -- Lua脚本实现Redis分布式锁 local lockKey = 'lock' local uuid = ARGV\[1\] if redis.call('get', lockKey) == uuid then redis.call('del', lockKey) return 1 else return 0 end ``` 这段代码首先定义了一个锁的键名为`lockKey`,然后通过传入的参数`ARGV\[1\]`获取到要删除的锁的UUID。接下来,它会通过`redis.call('get', lockKey)`来获取当前锁的值,如果与传入的UUID相等,则说明当前锁是由该UUID持有的,此时会使用`redis.call('del', lockKey)`来删除锁,并返回1表示删除成功。如果锁的值与传入的UUID不相等,则说明当前锁不是由该UUID持有的,此时直接返回0表示删除失败。 这段代码可以用于实现Redis分布式锁的原子性删除操作,确保只有持有锁的客户端才能删除锁,避免误删锁的问题。同时,使用Lua脚本可以保证删除锁的操作是原子性的,避免并发情况下的竞争问题。 #### 引用[.reference_title] - *1* *2* [Redis 实现分布式锁+执行lua脚本](https://blog.csdn.net/qq_34285557/article/details/129700808)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Redis的分布式锁问题(九)Redis + Lua 脚本实现分布式锁](https://blog.csdn.net/weixin_43715214/article/details/127982757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CoLiuRs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值