Redis optimistic lock with golang demo

lidashuang · · 1020 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:lidashuang

查看原文:Redis optimistic lock with golang demo

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1020 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传