亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何避免 GORM 中的競爭條件

如何避免 GORM 中的競爭條件

Go
皈依舞 2022-08-09 20:45:00
我正在開發一個系統,以啟用具有增量隊列編號的患者注冊。我正在使用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}
查看完整描述

1 回答

?
炎炎設計

TA貢獻1808條經驗 獲得超4個贊

正如 @O. Jones 所說,事務不會在這里保存你,因為你正在提取列的最大值,在 db 之外遞增它,然后保存該新值。從數據庫的角度來看,更新的值與查詢的值無關。


您可以嘗試在單個查詢中執行更新,這將使依賴關系變得明顯:


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 = ?;

現在,您可以在事務提交之前在患者創建/更新中使用遞增的隊列編號。


查看完整回答
反對 回復 2022-08-09
  • 1 回答
  • 0 關注
  • 104 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號