登录
首页 >  Golang >  Go问答

如何避免 GORM 中的竞争条件

来源:stackoverflow

时间:2024-04-15 15:54:34 202浏览 收藏

亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《如何避免 GORM 中的竞争条件》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下,希望所有认真读完的童鞋们,都有实质性的提高。

问题内容

我正在开发一个系统,可以使用增量队列号进行患者登记。我正在使用 go、gorm 和 mysql。

当多个患者同时登记时,就会出现问题,他们往往会获得相同的队列号码,这是不应该发生的。

我尝试使用事务和挂钩来实现这一目标,但我仍然得到重复的队列号。我还没有找到任何关于如何在事务发生时锁定数据库的资源。

func (r repository) CreatePatient(pat *model.Patient) error {
    tx := r.db.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()

    err := tx.Error
    if err != nil {
        return err
    }

    

    // 1. get latest queue number and assign it to patient object
    var queueNum int64
    err = tx.Model(&model.Patient{}).Where("registration_id", pat.RegistrationID).Select("queue_number").Order("created_at desc").First(&queueNum).Error

    if err != nil && err != gorm.ErrRecordNotFound {
        tx.Rollback()
        return err
    }
    pat.QueueNumber = queueNum + 1

    // 2. write patient data into the db
    err = tx.Create(pat).Error
    if err != nil {
        tx.Rollback()
        return err
    }

    return tx.Commit().Error
}

解决方案


正如@o所述。琼斯,事务不会将您保存在这里,因为您正在提取列的最大值,在数据库外部递增它,然后保存该新值。从数据库的角度来看,更新的值与查询的值没有依赖关系。

您可以尝试在单个查询中进行更新,这将使依赖性变得明显:

update patient as p
join (
  select max(queue_number) as queue_number from patient where registration_id = ?
) maxp
set p.queue_number = maxp.queue_number + 1 
where id = ?

在 gorm 中,您无法运行这样的复杂更新,因此您需要使用 exec

我不能 100% 确定上述方法是否有效,因为我不太熟悉 mysql 事务隔离保证。

更清洁的方式

总的来说,保存一个队列表(通过reference_id)和一个原子更新的计数器会更干净:

开始交易,然后

select queue_number from queues where registration_id = ? for update;

增加应用代码中的队列编号,然后

UPDATE queues SET queue_number = ? WHERE registration_id = ?;

现在,您可以在事务提交之前在患者创建/更新中使用增加的队列号。

终于介绍完啦!小伙伴们,这篇关于《如何避免 GORM 中的竞争条件》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>