站长资讯网
最全最丰富的资讯网站

详解Go库存扣减如何实现的(多种方法)

本文由golang教程栏目给大家介绍关于Go 库存扣减的几种实现方法,希望对需要的朋友有所帮助!

Go 库存扣减的几种实现方法

这里使用了 grpc、proto、gorm、zap、go-redis、go-redsync 等 package

Go Mutex 实现
var m sync.Mutexfunc (*InventoryServer) LockSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {     tx := global.DB.Begin()     m.Lock()      for _, good := range req.GoodsInfo {         var i model.Inventory        if result := global.DB.Where(&model.Inventory{Goods: good.GoodsId}).First(&i);               result.RowsAffected == 0 {             tx.Rollback() // 回滚             return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息。")         }         if i.Stocks < good.Num {             tx.Rollback()              return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足")         }         i.Stocks -= good.Num         tx.Save(&i)     }     tx.Commit()     m.Unlock()     return &emptypb.Empty{}, nil}
MySQL 悲观锁实现
func (*InventoryServer) ForUpdateSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {     tx := global.DB.Begin()     for _, good := range req.GoodsInfo {         var i model.Inventory        if result := tx.Clauses(clause.Locking{             Strength: "UPDATE",         }).Where(&model.Inventory{Goods: good.GoodsId}).First(&i);             result.RowsAffected == 0 {             tx.Rollback()             return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息。")         }         if i.Stocks < good.Num {             tx.Rollback()             return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足")         }          i.Stocks -= good.Num         tx.Save(&i)     }      tx.Commit()     return &emptypb.Empty{}, nil}
MySQL 乐观锁实现
func (*InventoryServer) VersionSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {     tx := global.DB.Begin()     for _, good := range req.GoodsInfo {         var i model.Inventory        for { // 并发请求相同条件比较多,防止放弃掉一些请求             if result := global.DB.Where(&model.Inventory{Goods: good.GoodsId}).First(&i);                 result.RowsAffected == 0 {                  tx.Rollback()                 return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息.")             }             if i.Stocks < good.Num {                 tx.Rollback() // 回滚                 return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足")             }             i.Stocks -= good.Num             version := i.Version + 1             if result := tx.Model(&model.Inventory{}).                 Select("Stocks", "Version").                 Where("goods = ? and version= ?", good.GoodsId, i.Version).                 Updates(model.Inventory{Stocks: i.Stocks, Version: version});                 result.RowsAffected == 0 {                                  zap.S().Info("库存扣减失败!")             } else {                 break             }         }     }     tx.Commit() // 提交     return &emptypb.Empty{}, nil}
Redis 分布式锁实现
func (*InventoryServer) RedisSell(ctx context.Context, req *proto.SellInfo) (*emptypb.Empty, error) {     // redis 分布式锁     pool := goredis.NewPool(global.Redis)     rs := redsync.New(pool)     tx := global.DB.Begin()     for _, good := range req.GoodsInfo {         mutex := rs.NewMutex(fmt.Sprintf("goods_%d", good.GoodsId))         if err := mutex.Lock(); err != nil {             return nil, status.Errorf(codes.Internal, "redis:分布式锁获取异常")         }         var i model.Inventory        if result := global.DB.Where(&model.Inventory{Goods: good.GoodsId}).First(&i); result.RowsAffected == 0 {             tx.Rollback()             return nil, status.Errorf(codes.InvalidArgument, "未找到此商品的库存信息")         }         if i.Stocks < good.Num {             tx.Rollback()             return nil, status.Errorf(codes.ResourceExhausted, "此商品的库存不足")         }         i.Stocks -= good.Num         tx.Save(&i)         if ok, err := mutex.Unlock(); !ok || err != nil {             return nil, status.Errorf(codes.Internal, "redis:分布式锁释放异常")         }     }     tx.Commit()     return &emptypb.Empty{}, nil}

测试

涉及到服务、数据库等环境,此测试为伪代码

func main() {   var w sync.WaitGroup   w.Add(20)   for i := 0; i < 20; i++ {       go TestForUpdateSell(&w) // 模拟并发请求   }   w.Wait()}func TestForUpdateSell(wg *sync.WaitGroup) {      defer wg.Done()   _, err := invClient.Sell(context.Background(), &proto.SellInfo{       GoodsInfo: []*proto.GoodsInvInfo{      {GoodsId: 16, Num: 1},   //{GoodsId: 16, Num: 10},       },   })   if err != nil {       panic(err)  }   fmt.Println("库存扣减成功")}

赞(0)
分享到: 更多 (0)