mysql:使用樂觀鎖保護資料一致性和完整性

若-飞發表於2024-08-05

在資料庫操作中,保持資料一致性和完整性至關重要。樂觀鎖(Optimistic Lock)是一種不鎖定資源的鎖機制,它在資料更新時才會檢測是否發生衝突。本文將介紹樂觀鎖的概念、使用方法、優缺點,並特別羅列它與悲觀鎖的區別。

樂觀鎖的概念

樂觀鎖基於這樣一個假設:資料衝突並不頻繁發生,因此在讀取資料時不對其加鎖,而在更新資料時檢測是否發生衝突。樂觀鎖通常透過在記錄中增加一個版本號(或時間戳)欄位來實現,在更新時檢查版本號是否一致。如果版本號不一致,則表示資料已經被其他事務修改,此時更新操作會失敗。

樂觀鎖是業務實現的,不加鎖。

樂觀鎖的使用示例

以下示例展示瞭如何在使用GORM(一個流行的Go語言ORM框架)時應用樂觀鎖:

// 樂觀鎖
result := tx.Model(&NifishuntGame.TgUser{}).Where("status=?", "pending").
    Update("status", "success"))
if result.Error != nil {
    log.Println("Update Status Error:", result.Error)
    return result.Error
}

if result.RowsAffected == 0 {
    return errors.New("status_is_not_update")
}

在這個示例中:

我們在更新status欄位時檢查status欄位是否匹配,如果不匹配則表示資料已經被修改,從而避免了併發衝突。

如果result.RowsAffected!=0,表示獲得了這個鎖,可以進一步完善其他邏輯功能(比如完成發放獎金等等)

如果result.RowsAffected==0,表示這個狀態已經被其他協程處理了,這裡預設就可以不處理了(因為已經有人在處理了)。

想下另外一個需求: 假設資料庫有個欄位i=1, A/B兩使用者都想給i+1,採用樂觀鎖會遇到什麼問題?

  1. 可能有一個人成功,另外一個人不成功,這時候不成功的人需要重試,以解決衝突問題

  2.需要採用最終一致性

樂觀鎖的優點

  1. 高併發效能:樂觀鎖不在讀取資料時加鎖,因此對併發效能影響較小,適用於高併發環境。
  2. 實現簡單:不需要維護複雜的鎖機制,只需在更新時進行衝突檢測即可。
  3. 減少死鎖風險:由於樂觀鎖不加鎖,因此不會產生死鎖問題。

樂觀鎖的缺點

  1. 衝突處理複雜在更新時如果檢測到衝突,需要處理衝突並重試更新操作,這增加了應用程式的複雜性。
  2. 適用場景有限:樂觀鎖適用於資料衝突較少的場景,如果資料衝突頻繁發生,樂觀鎖的重試機制可能會導致效能下降。

樂觀鎖與悲觀鎖的區別

  1. 鎖定時機

    • 樂觀鎖:在讀取資料時不加鎖,僅在更新資料時檢測衝突。
    • 悲觀鎖:在讀取資料時即加鎖,確保其他事務無法同時修改該資料。
  2. 效能影響

    • 樂觀鎖:對併發效能影響較小,適用於高併發環境。
    • 悲觀鎖:對併發效能影響較大,適用於資料一致性要求高的環境。
  3. 死鎖風險

    • 樂觀鎖:由於不加鎖,不會產生死鎖問題。
    • 悲觀鎖:可能產生死鎖,需要小心設計和處理。
  4. 實現複雜度

    • 樂觀鎖:實現相對簡單,但需要處理衝突和重試邏輯。
    • 悲觀鎖:實現較複雜,需要維護鎖的狀態和順序。

適用場景

樂觀鎖適用於以下場景:

  1. 高併發環境:如社交網路、線上遊戲等使用者併發操作頻繁的場景。
  2. 資料衝突較少:適用於資料更新衝突較少的場景,如使用者個人資訊更新等。

樂觀鎖適合用在不容易出現衝突的情況,比如使用者的個人資料上面;如果是全域性的資料必須採用悲觀鎖

結論

樂觀鎖是一種高效的併發控制機制,適用於高併發、低衝突的場景。相比悲觀鎖,樂觀鎖對系統效能的影響較小,但需要處理資料衝突的情況。在實際應用中,應根據具體場景和需求選擇合適的鎖機制,以實現最佳的效能和資料一致性。

希望本文對你理解和使用樂觀鎖有所幫助。如果你有任何問題或建議,歡迎留言討論。

相關文章