在Golang中避免竞争条件的情况下递增不同记录中的字段值

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

Increment field value in different records avoiding race condition in Golang

问题

我的 Golang 代码使用 goroutines 从数据库中获取不同的记录,并在记录中的一个确定字段中递增值。

如果我使用 Mutex 或 Channels,我可以避免竞争条件,但是由于每次访问数据库都要等待前一次访问完成,所以存在瓶颈。

我认为我应该为每个不同的记录使用一个 Mutex,而不是只有一个 Mutex。

我应该如何做呢?

谢谢任何回复。

英文:

My Golang code gets different records from the database using goroutines, and increments the value in a determinated field in the record.

I can avoid the race condition If I use Mutex or Channels, but I have a bottleneck because every access to the database waits until the previous access is done.

I think I should do something like one Mutex for every different record, instead one only Mutex for all.

How could I do it?

Thanks for any reply.

答案1

得分: 1

在你的评论中,你说你正在使用Couchbase。如果你想要更新的记录只包含一个整数,你可以使用内置的原子递增功能Bucket.Incr

如果该值是一个较大文档的一部分,你可以使用数据库的“检查和设置”功能。实质上,你需要创建一个循环,执行以下操作:

  1. 使用Bucket.Gets检索要更新的记录以及其CAS值。
  2. 根据(1)返回的文档进行必要的修改。
  3. 使用Bucket.Cas存储修改后的文档,传递在(1)中检索到的CAS值。
  4. 如果(4)成功,则跳出循环。否则,重新开始(1)。

请注意,在循环中每次都重复检索文档非常重要。如果由于CAS值不正确而更新失败,这意味着在读取和写入之间文档已被更新。

英文:

In the comments you said you are using Couchbase. If the record you wish to update consists of only an integer, you can use the built in atomic increment functionality with Bucket.Incr.

If the value is part of a larger document, you can use the database's "Check and Set" functionality. In essence, you want to create a loop that does the following:

  1. Retrieve the record to be updated along with its CAS value using Bucket.Gets
  2. Modify the document returned by (1) as needed.
  3. Store the modified document using Bucket.Cas, passing the CAS value retrieved in (1).
  4. If (4) succeeds, break out of the loop. Otherwise, start again at (1).

Note that it is important to retrieve the document fresh each time in the loop. If the update fails due to an incorrect CAS value, it means the document was updated between the read and the write.

答案2

得分: 0

如果数据库有处理这个的方法(即内置的原子计数器),你应该使用它。
如果没有,或者如果你想在Go语言中完成这个操作,你可以使用带缓冲的通道。向带缓冲的通道插入数据不会阻塞,除非缓冲区已满。

然后,为了逐个处理增量,在一个goroutine中,你可以有类似以下的代码:

for {
    select {
        case value, _ := <-CounterChan:
            incrementCounterBy(value)
    }
}
英文:

If the database has a way of handling that (i.e. an atomic counter built in) you should use that.
If not, or if you want to do this in go, you can use buffered channels. Inserting to a buffered channel is not blocking unless the buffer is full.

then, to handle the increments one at a time, in a goroutine you could have something like

<!-- language: go -->

for{
    select{
        value, _ := &lt;-CounterChan
        incrementCounterBy(value)
    }
}

huangapple
  • 本文由 发表于 2014年6月23日 16:53:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/24361881.html
匿名

发表评论

匿名网友

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

确定