首页 > 其他分享 >go库存扣减的几种方法

go库存扣减的几种方法

时间:2023-03-16 10:22:05浏览次数:41  
标签:good 扣减 tx nil 几种 Errorf go return Stocks

用编码工具,建议用最新版Goland,因为我有正式版激活码

https://www.mano100.cn/thread-1942-1-1.html

 

现在就开始你的Go语言学习之旅吧!人生苦短,let’s 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("库存扣减成功")}

 

 

 

 

文章首发:https://www.php.cn/be/go/487728.html

 

 

 

 

 

 

更多相关Go语言的技术文章或视频教程,请关注本公众号获取并查看,感谢你的支持与信任!

标签:good,扣减,tx,nil,几种,Errorf,go,return,Stocks
From: https://www.cnblogs.com/cheyunhua/p/17221315.html

相关文章

  • Vscode golang初始化配置
    1、下载安装GO插件2、写代码时vscode提示你需要安装go插件,点击installall进行安装如果安装失败,原因是有道墙,解决方法是,配置国内源如下:goenv-wGO111MODULE=ongoe......
  • 重构:banner 中 logo 聚合分散动画
    1.效果展示在线查看2.开始前说明效果实现参考源码:Logo聚集与散开原效果代码基于reactjsx类组件实现。依赖旧,代码冗余。我将基于此进行重构,重构目标:基于最新......
  • Go——语言特性
    golang简介来历很久以前,有一个IT公司,这公司有个传统,允许员工拥有20%自由时间来开发实验性项目。在2007的某一天,公司的几个大牛,正在用c++开发一些比较繁琐但是核心的工作......
  • 40. CF-Not So Simple Polygon Embedding
    链接题解里的几何做法很巧妙,这里记录一下。因为有\(2n\)条边,每条边对应的角度就是\(\dfrac{2\pi}{2n}\)。考虑对角线与底边平行的状态。顺时针或逆时针转动\(\dfrac......
  • #yyds干货盘点#iframe跨域的几种常用方法
    在开发日常中,会遇到使用iframe嵌套其他页面,想要与嵌套页面进行交互,常常会涉及到跨域问题,何为跨域?这涉及到同源策略,即协议、端口、域名相同则为同源违反了同源策略就会出现跨......
  • Go(2)
    统计1出现的频率string.Count(str,x)//str:是待查找的字符串//x:可以是一个字符串也可以是一个变量,反应的是变量的值2字符数量len([]rune(str))或import "unicod......
  • Django应用之间模型字段关联(实现表与表解耦)
    serializers.SerializerMethodField和钩子方法结合,可以实现对ModelSerializer类的一些字段进行二次加工classtbl_group_case(models.Model):STATUS_NORMAL=0......
  • 开心档之Go 语言常量
    Go语言常量常量是一个简单值的标识符,在程序运行时,不会被修改的量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。常量的定义格式:constid......
  • vue + djangorestframework實現文件下載功能
    1.安裝模塊及配置及配置先安裝django-cors-headers包pip3installdjangorestframeworkdjango-cors-headers 在setting文件中註冊appINSTALLED_APPS=['djang......
  • go开发入门篇之go语言
    ​​ Go语言提供了数组类型的数据结构。数组是一组已编号的、长度固定的数据项序列,具有相同的唯一类型,可以是任意的原始类型,例如整型、字符串或自定义类型。相对于......