前言:
單臺伺服器的時候,使用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 協議》,轉載必須註明作者和本文連結