Redis-optimistic-lock-with-golang-demo

5次阅读

共计 2045 个字符,预计需要花费 6 分钟才能阅读完成。

redis 事务处理命令

  • MULTI:开启一个事务
  • EXEC:事务执行,将一次性执行事务内的所有命令
  • DISCARD:取消事务

使用 WATCH+MULTI 的方式来实现乐观锁

WATCH:监控一个或多个键,如果事务执行前某个键发生了改动,那么事务也会被打断
UNWATCH:取消 WATCH 命令对所有键的监视

使用 go-redis package 模拟用户抢票的流程

  • 开启多个 goroutine 模拟并发抢票
  • go-redis TxPipelined 执行事务
  • go-redis client.Watch 监控某个键
package main

import (
    "errors"
    "fmt"
    "sync"

    "github.com/go-redis/redis/v7"
)

func main() {
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    key := "ticket_count"
    client.Set(key, "5", 0).Err()
    val, _ := client.Get(key).Result()
    fmt.Println("current ticket_count key val:", val)

    getTicket(client, key)
}

func runTx(key string, id int) func(tx *redis.Tx) error {txf := func(tx *redis.Tx) error {n, err := tx.Get(key).Int()
        if err != nil && err != redis.Nil {return err}

        if n == 0 {return errors.New("票没了")
        }

        // actual opperation (local in optimistic lock)
        n = n - 1

        // runs only if the watched keys remain unchanged
        _, err = tx.TxPipelined(func(pipe redis.Pipeliner) error {
            // pipe handles the error case
            pipe.Set(key, n, 0)
            return nil
        })
        return err
    }
    return txf
}

func getTicket(client *redis.Client, key string) {
    routineCount := 8
    var wg sync.WaitGroup
    wg.Add(routineCount)

    for i := 0; i < routineCount; i++ {go func(id int) {defer wg.Done()

            for {err := client.Watch(runTx(key, id), key)
                if err == nil {fmt.Println(id, "成功")
                    return
                } else if err.Error() == "票没了" {fmt.Println(id, "票没了")
                    return
                } else {fmt.Println(err, "retry")
                }
            }
        }(i)
    }
    wg.Wait()}

Github code: https://github.com/defp/redis…

current ticket_count key val:  5
7 成功
6 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
3 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
2 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
5 成功
redis: transaction failed retry
redis: transaction failed retry
redis: transaction failed retry
4 票没了
1 票没了
0 票没了 

links

  • How to create Redis Transaction in Go using go-redis/redis package? – Stack Overflow
  • redis package · pkg.go.dev
  • Redis 写缓存
  • defp/redis-watch-multi-exec-demo
  • 42 丨如何使用 Redis 来实现多用户抢票问题
正文完
 0