分布式服务数据一致性-mysql篇
前言:
单台服务器的时候,使用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 协议》,转载必须注明作者和本文链接
推荐文章: