使用go-redis/redis包在Go中如何创建Redis事务?

huangapple go评论72阅读模式
英文:

How to create Redis Transaction in Go using go-redis/redis package?

问题

我想使用MULTIEXEC命令执行多个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(&quot;tx_pipeline_counter&quot;)
pipe.Expire(&quot;tx_pipeline_counter&quot;, 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 &lt;nil&gt;

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 &amp;&amp; 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 &gt; 0; retries-- {
err := rdb.Watch(txf, key)
if err != redis.TxFailedErr {
return err
}
// optimistic lock lost
}
return errors.New(&quot;increment reached maximum number of retries&quot;)
}
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i &lt; routineCount; i++ {
go func() {
defer wg.Done()
if err := increment(&quot;counter3&quot;); err != nil {
fmt.Println(&quot;increment error:&quot;, err)
}
}()
}
wg.Wait()
n, err := rdb.Get(&quot;counter3&quot;).Int()
fmt.Println(&quot;ended with&quot;, n, err)

Output:

ended with 100 &lt;nil&gt;

huangapple
  • 本文由 发表于 2016年12月6日 23:49:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/40999446.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定