乐观锁
乐观锁是一种乐观的并发控制策略,它假设在多个事务同时访问同一数据时,发生数据冲突的可能性较小。因此,在数据访问过程中,乐观锁不会加锁阻塞其他事务的读取操作,而是在数据更新时通过检查数据的版本号或时间戳等机制来判断数据是否已被其他事务修改过,从而决定是否进行更新。
优点:
高并发性能:由于不阻塞其他事务的读取操作,因此可以提供更高的并发性能。
无锁开销:避免了加锁和释放锁的开销,减少了系统的负担。
无死锁风险:由于不会阻塞其他事务的访问,因此不会出现死锁的情况。
缺点:
冲突处理复杂:在提交时需要检查数据是否被其他事务修改,如果发现冲突,需要设计合适的处理策略,这增加了实现的复杂性。
数据一致性风险:如果多个事务同时对同一数据进行修改,且乐观锁的实现不当或处理冲突的策略不合理,可能会导致数据不一致的情况。
需要额外字段:为了实现乐观锁,通常需要在数据表中添加额外的版本号或时间戳字段,这增加了存储空间的需求。
// Sell 扣减库存 乐观锁实现
func (c *Server) Sell(ctx context.Context, in *proto.SellInfo) (*proto.Empty, error) {
//开启本地事务
//并发情况下出现超卖的问题 要确保他的一致性 一起成功或者一起失败
for _, info := range in.GoodsInfo {
for {
//定义一个库存存放的结构体
//查询是否有这个库存
var inv model.Inventory
result := global.Db.Where(&model.Inventory{GoodsId: info.GoodsId}).First(&inv)
if result.RowsAffected != 1 || result.Error != nil {
return nil, status.Errorf(codes.Internal, "库存详情查询失败")
}
if inv.Number < info.Num {
return nil, status.Errorf(codes.Internal, "库存不足")
}
//扣减,会出现数据不一致的问题
res := global.Db.Select("number", "version").Where("goods_id = ? and version = ?", inv.GoodsId, inv.Version).Updates(model.Inventory{
Number: inv.Number - info.Num,
Version: inv.Version + 1,
})
if res.RowsAffected == 1 {
break
}
}
}
return &proto.Empty{}, nil
}
乐观锁的重点
注意在for循环里面
res := global.Db.Select("number", "version").Where("goods_id = ? and version = ?", inv.GoodsId, inv.Version).Updates(model.Inventory{
Number: inv.Number - info.Num,
Version: inv.Version + 1,
})
if res.RowsAffected == 1 {
break
}