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 来实现多用户抢票问题