Web前端培训_Web前端实战培训【立即报名】-php中文网Web前端第二期
推荐视频教程
  • Go语言教程手册Go语言教程手册
  • go语言基础与基本函数go语言基础与基本函数
  • 视频教程分类
    首页 >后端开发 >Golang > 正文

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

    转载2022-01-18 16:07:2701194
    本文由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("库存扣减成功")}

    以上就是详解Go库存扣减如何实现的(多种方法)的详细内容,更多请关注php中文网其它相关文章!

    Web大前端开发直播班

    声明:本文转载于:learnku,如有侵犯,请联系admin@php.cn删除

  • 相关标签:Go
  • 相关文章

    相关视频


    网友评论

    文明上网理性发言,请遵守 新闻评论服务协议

    我要评论
  • 专题推荐