本文由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库存扣减如何实现的(多种方法)的详细内容。