Redis optimistic lock with golang demo

redis 事務處理命令git

  • MULTI:開啓一個事務
  • EXEC:事務執行,將一次性執行事務內的全部命令
  • DISCARD:取消事務

使用 WATCH+MULTI 的方式來實現樂觀鎖 github

WATCH:監控一個或多個鍵,若是事務執行前某個鍵發生了改動,那麼事務也會被打斷
UNWATCH:取消 WATCH 命令對全部鍵的監視redis

使用go-redis package模擬用戶搶票的流程c#

  • 開啓多個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

相關文章
相關標籤/搜索