讲一下对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没有发生修改要执行的事务逻辑
- 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
方法会自动重新执行回调函数,直到事务成功提交或达到某些终止条件(例如最大尝试次数)。