千家信息网

Go库存扣减怎么实现

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要介绍了Go库存扣减怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go库存扣减怎么实现文章都会有所收获,下面我们一起来看看吧。Go 库存扣减的几种实现方
千家信息网最后更新 2026年11月03日Go库存扣减怎么实现

这篇文章主要介绍了Go库存扣减怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇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库存扣减怎么实现"这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对"Go库存扣减怎么实现"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。

0