分散式服務資料一致性-mysql篇

Yoger發表於2022-02-10

前言:

單臺伺服器的時候,使用mutex/RWMutex都可以給goroutine加鎖,當服務部署到多臺伺服器時,mutex/RWMutex不能解決併發問題,需要分散式鎖解決。

mysql解決方案:

1.悲觀鎖方案

#向mysql請求一把鎖 for update ,使用for update的時候注意:預設每個語句mysql都是自動提交的
SELECT @@autocommit;
#需要關閉autocommit
SET autocommit=0;
#符合條件的資料會被行鎖,如果條件欄位沒有索引則會發生表鎖,在沒有滿足條件的情況下,有索引不鎖表,無索引鎖全表
SELECT * FROM goods WHERE product_id = 1 FOR UPDATE;
……需要執行的邏輯程式碼
#釋放這把鎖
COMMIT;
#如果不加for update關鍵字是不會被鎖的,類似讀寫鎖
SELECT * FROM goods WHERE product_id = 1 FOR UPDATE;
//注意事項:需要開啟事務手動提交釋放鎖
var wg sync.WaitGroup
wg.Add(20)
for i := 0; i < 20; i++ {
   // 開始事務
   go func() {
      var goods Goods
      tx := db.Begin()
      tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where(Goods{ProductId: 1}).First(&goods)
      goods.Inventory -= 1
      tx.Save(&goods)
      fmt.Println("庫存數量",goods.Inventory)
      tx.Commit()
      wg.Done()
   }()
}
wg.Wait()

2.樂觀鎖方案

//透過版本號或者時間戳確認,不符合條件重新執行。沒有用到鎖,可以避免出現死鎖
var wg sync.WaitGroup
wg.Add(20)
for i := 0; i < 20; i++ {
   go func() {
      var goods Goods
      for  {
         db.Where(Goods{ProductId: 1}).First(&goods)
         //最後一個庫存扣減會失敗,gorm會忽略零值的欄位,新增select選擇要更新的欄位,或者使用Save方法儲存
         result := db.Model(&Goods{}).Select("Inventory","Version").
            Where("product_id=? and version=?",1,goods.Version).
            Updates(Goods{Inventory: goods.Inventory - 1, Version: goods.Version + 1})
         if result.RowsAffected == 0 {
            fmt.Println("庫存扣減失敗")
         }else {
            break
         }
      }
      fmt.Println("庫存數量", goods.Inventory)
      wg.Done()
   }()
}
wg.Wait()
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章