Golang并发模式下数据一致性问题

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

前言

在Golang中,并发编程变得容易实现,仅需要起一个goroutine即可开启并发模式:

go f("goroutine")

但是,在并发编程中存在不少需要注意的地方。这篇文章简单讨论下,在多个goroutine对MongoDB数据库进行操作时,保证操作一致性的方法。


问题描述

假设我们起了多个goroutine,如下:

go func() {
       //step1: 获取所有 status==true 的记录
       records, _ := models.Record.FindStatus(true)
       for _, v := records{
           //step2: then we do something about record
           v.DoSomething()
           //step3: 将这些记录的 status 更新为 false
           v.status = false
           if err := v.Save(); err != nil{
               return
           }
       }
}()


以上代码从数据库中取得所有 `status == true` 的记录,随后对这些记录进行操作,最终将记录的状态设置为 `false`。

仔细一看,这段代码明显存在问题,描述如下:

  • 假设有两个goroutine: goroutineA, goroutineB 都进行了以上操作
  • goroutineA 执行了step1 取出状态为 `true` 的记录,并且进行了 step2 操作
  • 当 goroutineA 执行到 step2 但是 step3 还未执行完成时,goroutineB 也有可能成功执行 step1。随后goroutineB 也执行了 step2。显然,这不是我们所预期的结果。


解决方案

解决问题的主要思路是:

  • 为 status 多设置一个中间状态 pending,代表该记录正在被处理中
  • 添加SetStatus方法,在Update的时候确保 `status`字段未被其他事务改动

参考代码如下:

type RecordStatus int
const(
   StatusBefore RecordStatus = iota
   StatusPending
   StatusAfter
)
func (record *RecordModel) SetStatus(status RecordStatus) (err error){
    Record.Query(func(c *mgo.Collection) {
   	query := bson.M{
  	       "_id":    Domain Premium Untuk Bisnis Anda,
   	       "status": record.Status,
   	}
        update := bson.M{
   	       "status":     status,
   	       "updated_at": time.Now(),
   	}
   	err = c.Update(query, bson.M{
   	       "$set": update,
   	})
   	if err == nil {
   		record.Status = status
   	}
   })
   return
}
go func() {
       //step1: 获取所有 status==true 的记录
       records, _ := models.Record.FindStatus(true)
       for _, v := records{
           // step2: set record status to be "pending"
           if err := v.SetStatus(StatusPending); err != nil{
               return
           }
           //step3: then we do something about record
           v.DoSomething()
           //step4: 将这些记录的 status 更新为 false
          if err := v.SetStatue(StatusAfter); err != nil{
              return
          }
       }
}()


通过以上方法可以确保不会同时有多个goroutine 对一条数据进行了更新操作。


我的微信公众号: EasyHacking

Go设计模式陆续更新中 github: csxuejin/go-design-patterns


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

本文来自:知乎专栏

感谢作者:薛锦

查看原文:Golang并发模式下数据一致性问题

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

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