为了账号安全,请及时绑定邮箱和手机立即绑定

Go Gorm 对增量计数器的原子更新

Go Gorm 对增量计数器的原子更新

Go
SMILET 2022-06-01 15:21:49
我有一个本质上是用户可以增加的计数器。但是,我想避免两个用户同时增加计数器的竞争条件。有没有办法使用 Gorm 原子地增加一个计数器,而不是从数据库中获取值,增加,最后更新数据库?
查看完整描述

2 回答

?
潇湘沐

TA贡献1816条经验 获得超6个赞

如果要使用基本的 ORM 功能,可以在检索记录时使用FOR UPDATE查询选项,数据库将锁定该特定连接的记录,直到该连接发出UPDATE查询以更改该记录。

SELECTUPDATE语句必须发生在同一个连接上,这意味着您需要将它们包装在一个事务中(否则 Go 可能会通过不同的连接发送第二个查询)。

请注意,这将使所有其他想要SELECT相同记录的连接等到您完成UPDATE. 对于大多数应用程序来说,这不是问题,但如果您的并发性非常高,或者两者之间的时间SELECT ... FOR UPDATEUPDATE长,那么这可能不适合您。

除了 之外FOR UPDATE,该FOR SHARE选项听起来也可以为您工作,锁定争用较少(但我不太清楚,不能肯定地说)。

注意:这假设您使用支持的 RDBMS SELECT ... FOR UPDATE; 如果没有,请更新问题以告诉我们您正在使用哪个 RDBMS。

另一种选择是绕过 ORM 并执行db.Exec("UPDATE counter_table SET counter = counter + 1 WHERE id = ?", 42)


查看完整回答
反对 回复 2022-06-01
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

一种可能的解决方案是使用 GORM 事务(https://gorm.io/docs/transactions.html)。


    err := db.Transaction(func(tx *gorm.DB) error {

        // Get model if exist

        var feature models.Feature

        if err := tx.Where("id = ?", c.Param("id")).First(&feature).Error; err != nil {

            return err

        }


        // Increment Counter

        if err := tx.Model(&feature).Update("Counter", feature.Counter+1).Error; err != nil {

            return err

        }


        return nil

    })


    if err != nil {

        c.Status(http.StatusInternalServerError)

        return

    }


    c.Status(http.StatusOK)


查看完整回答
反对 回复 2022-06-01
  • 2 回答
  • 0 关注
  • 267 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号