讲一下对watch的理解
在redis中watch是通过加了乐观锁的方法先获取事务执行前key值,如果在开始执行事务时检测到由于并发key被其他客户端修改了,就会终止事务
redis-cli //启动连接redis
WATCH counter//watch监听 counter
MULTI//如果counter 没有变化就开启事务
INCR counter//让counter自增
EXEC//提交事务
如果用go-redis实现就是这样的
var rdb *redis.Client
var ctx = context.Background() //上下文
/*
这部分代码创建了一个 Redis 客户端实例,连接到本地的 Redis 服务器。在实际应用中,
你可能需要根据实际情况配置 Redis 的地址、密码和数据库。
*/
func init() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
}
// 定义一个回调函数,用于处理事务逻辑
fn := func(tx *redis.Tx) error {
// 先查询下当前watch监听的key的值
v, err := tx.Get(ctx, "key").Int()
if err != nil && err != redis.Nil {
return err
}
// 这里可以处理业务
v++
// 如果key的值没有改变的话,Pipelined函数才会调用成功
_, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
// 在这里给key设置最新值
pipe.Set(ctx, "key", v, 0)
result, _ := tx.Get(ctx, "key").Result()
fmt.Println(result+" ", v)
return nil
})
return err
}
// 使用Watch监听一些Key, 同时绑定一个回调函数fn, 监听Key后的逻辑写在fn这个回调函数里面
// 如果想监听多个key,可以这么写:client.Watch(ctx,fn, "key1", "key2", "key3")
rdb.Watch(ctx, fn, "key")
其中watch即使对key值的监听,fn回调函数相当于如果key没有发生修改要执行的事务逻辑
下面这段代码为redis的连接实例化一个rdb客户端
var rdb *redis.Client
var ctx = context.Background() //上下文
/*
这部分代码创建了一个 Redis 客户端实例,连接到本地的 Redis 服务器。在实际应用中,
你可能需要根据实际情况配置 Redis 的地址、密码和数据库。
*/
func init() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
}
watch的执行逻辑
在go-redis中,当你使用Watch
函数并传入回调函数时,库的内部实现大致执行了以下步骤:
监控阶段:调用
Watch
函数时,它会首先为指定的键设置监视点(WATCH
命令)。这个监视确保如果在事务执行前这些键被修改,事务将不会执行。执行回调函数:如果在执行事务的过程中监视的键未被修改,则
Watch
函数会创建一个新的事务(MULTI
命令),并执行提供的回调函数。在回调函数内,你可以向事务中添加想要执行的命令。事务提交:回调函数执行完后,如果没有出现错误,事务将会被提交(
EXEC
命令)。如果在执行回调函数的过程中出现了错误,或者监视的键值被修改,则事务将会被取消(DISCARD
命令)。重试逻辑:如果由于
WATCH
监视的键被修改而导致事务失败,Watch
方法会自动重新执行回调函数,直到事务成功提交或达到某些终止条件(例如最大尝试次数)。