英文:
How to create Redis Transaction in Go using go-redis/redis package?
问题
我想使用MULTI
和EXEC
命令执行多个Redis命令,这样如果出现问题,我可以使用DISCARD
命令取消事务。
我在寻找如何使用go-redis/redis包进行Redis事务的示例,但没有找到。
我还查看了这里的文档,但没有找到有关如何使用该包进行Redis事务的相关信息,例如这个例子。或许我在文档中漏掉了一些内容,因为你知道,godoc通常只用一行来解释每个函数。
虽然我找到了一些使用其他Go Redis库进行Redis事务的示例,但我不想修改我的程序来使用另一个库,因为这将需要更大的工作量来迁移整个应用程序。
有人可以帮助我使用go-redis/redis包来实现吗?
英文:
I want to execute multiple redis commmand with transaction using MULTI
and EXEC
, so I can DISCARD
it if something bad happens.
I've looking for the example of how to do redis transaction using
go-redis/redis package and found nothing.
And I also look into the documentation here and I got nothing related to how to do redis transaction like this for example using that package. Or maybe I missing something from the documentation because yeah you know that godoc is only explain every function in package mostly using one liner.
Even though I found some example to do redis transaction using other Go Redis library, I won't modify my programs to use another library since the effort will be much larger to port whole application using another library.
Can anyone help me to do that using go-redis/redis package?
答案1
得分: 15
当你使用Client.Watch
方法时,会得到一个Tx
类型的值,用于表示一个事务。
以下是示例代码:
err := client.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe *redis.Pipeline) error {
pipe.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
return err
}, key)
请注意,这是一个示例代码,用于展示如何使用Client.Watch
方法进行事务操作。具体的实现可能会根据你的需求有所不同。
英文:
You get a Tx
value for a transaction when you use Client.Watch
err := client.Watch(func(tx *redis.Tx) error {
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Pipelined(func(pipe *redis.Pipeline) error {
pipe.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
return err
}, key)
答案2
得分: 13
你可以在这里找到一个创建Redis事务的示例:
代码:
pipe := rdb.TxPipeline()
incr := pipe.Incr("tx_pipeline_counter")
pipe.Expire("tx_pipeline_counter", time.Hour)
// 执行
//
// MULTI
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
// EXEC
//
// 只需要一个往返的rdb-server请求。
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
输出:
1 <nil>
如果你更喜欢使用watch,你可以在这里看到一个示例:
代码:
const routineCount = 100
// 使用GET和SET命令事务性地增加键值。
increment := func(key string) error {
txf := func(tx *redis.Tx) error {
// 获取当前值或零值
n, err := tx.Get(key).Int()
if err != nil && err != redis.Nil {
return err
}
// 实际操作(在乐观锁定中本地执行)
n++
// 仅在监视的键保持不变的情况下运行
_, err = tx.TxPipelined(func(pipe redis.Pipeliner) error {
// pipe处理错误情况
pipe.Set(key, n, 0)
return nil
})
return err
}
for retries := routineCount; retries > 0; retries-- {
err := rdb.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
// 乐观锁定失败
}
return errors.New("增加操作达到最大重试次数")
}
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ {
go func() {
defer wg.Done()
if err := increment("counter3"); err != nil {
fmt.Println("增加操作错误:", err)
}
}()
}
wg.Wait()
n, err := rdb.Get("counter3").Int()
fmt.Println("最终结果:", n, err)
输出:
最终结果: 100 <nil>
英文:
You can find an example how to create a Redis transaction here:
Code:
pipe := rdb.TxPipeline()
incr := pipe.Incr("tx_pipeline_counter")
pipe.Expire("tx_pipeline_counter", time.Hour)
// Execute
//
// MULTI
// INCR pipeline_counter
// EXPIRE pipeline_counts 3600
// EXEC
//
// using one rdb-server roundtrip.
_, err := pipe.Exec()
fmt.Println(incr.Val(), err)
Output:
1 <nil>
And if you prefer to use the watch (optimistic locking)
You can see an example here
Code:
const routineCount = 100
// Transactionally increments key using GET and SET commands.
increment := func(key string) error {
txf := func(tx *redis.Tx) error {
// get current value or zero
n, err := tx.Get(key).Int()
if err != nil && err != redis.Nil {
return err
}
// actual opperation (local in optimistic lock)
n++
// 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
}
for retries := routineCount; retries > 0; retries-- {
err := rdb.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
// optimistic lock lost
}
return errors.New("increment reached maximum number of retries")
}
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ {
go func() {
defer wg.Done()
if err := increment("counter3"); err != nil {
fmt.Println("increment error:", err)
}
}()
}
wg.Wait()
n, err := rdb.Get("counter3").Int()
fmt.Println("ended with", n, err)
Output:
ended with 100 <nil>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论