关于乐观锁占用mysql连接的疑惑

关于乐观锁占用mysql连接的疑惑

老师,关于这里业务代码,我贴了我写的demo的核心的代码如下:

大概是这样的逻辑

tx := db.DB.Begin()
for _, goodsInfo := range req.GoodsInfo {
   for {
      inv := db.Inventory{}
      // 这里实现乐观锁的时候,查询数据,一定不要用事务!不然拿不到最新的数据!
      result := db.DB.Where(map[string]interface{}{"goods": 1}).Limit(1).Find(&inv)
      
      //省略一些错误判断
      
      // 扣减库存
      inv.Stocks -= goodInfo.Num
      
      // 指定更新version=查询到的version值。并且此操作也要对version进行+1
      res := tx.Model(&db.Inventory{}).Select("stocks", "version").
         Where("goods = ? AND version = ?", inv.Goods, inv.Version).
         Updates(db.Inventory{
            Stocks:  inv.Stocks,
            Version: inv.Version + 1,
         })
      if res.Error != nil {
         tx.Rollback()
         fmt.Printf("商品库存更新失败: %+v", result.Error)
         return
      }
      if res.RowsAffected == 1 {
         break
      }
   }
}

tx.Commit()

这里在for循环最外面开启了事务,然后里面每个商品都死循环去扣减库存,是不是意味着,在循环等待的时候占用了mysql的一个连接?


因为一般情况我们在项目里使用gorm,都会设置连接池参数的,比如:

DB, err = gorm.Open(mysql.Open(dsn))

sqlDB, err := DB.DB()

// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)


那么我的问题是:

1.并发超过100个扣库存,是不是就把连接给全部占满了?服务就挂了? 我实测并发高一点就卡住了,如果不加连接池设置,那么比如并发500,就会有一段时间,500个开启事务的连接保持着,甚至并发更高,mysql就会报错max_connection了,怎么解决这种问题呢?或者说我这里实现方式有问题吗?


2. 如果我把事务放在for死循环里面,就是每次去update的时候再开启事务,关闭事务,确实连接不会占用了,但是for循环就会不断给mysql发送事务,这样每个update都超级慢。应该是不能这样使用的。


我写乐观锁的demo,就发现,它不如悲观锁性能好,悲观锁简单并发5000个都能很轻松跑完demo。


实际真正开发项目,写代码应该是不能这样写的吧?

请老师指教~














正在回答 回答被采纳积分+1

登陆购买课程后可参与讨论,去登陆

1回答
bobby 2023-03-29 21:27:21

连接池客户端和服务端都有,比如你的gorm连接池是10个,但是mysql的服务端的连接池是5个。那么你还是有5个需要等待。一般mysql默认的连接池很少,你可以百度一下如何查看mysql的连接池大小。你可以适当调大一下mysql的服务端连接池

  • 提问者 爱吃apple的阿狸 #1

    老师,我觉得这里代码有问题,老师的代码是没有加gorm的连接池配置的。

    实际项目肯定会加的吧。就是在有这个最大连接数配置的情况下:


    我不知道我描述的清晰不:

    假如我这里设置了gorm的连接池为最大5个,那么如果同时有5个请求进来了,

    每个请求都会开启一个事务,就会占用一个mysql连接,此时for循环里面的那个全局DB查询就拿不到Mysql连接了,5个for循环都卡在select那儿了,这是不是就卡死了啊? 

    gorm一旦设置了最大连接数,是不会扩大的。


    所以只要并发数超过我mysql的连接池数量就必然挂了啊。


    当然,如果项目不设置连接池最大的连接数量,那不会死锁,只是压力给到了mysql。

    2023-03-30 16:13:49
  • bobby 回复 提问者 爱吃apple的阿狸 #2

    事务的开启不是gorm做的,而是mysql服务器维护的,所以你的一个连接可以开启两个事务,事务的管理是由mysql 的server管理的, 只是gorm的连接池会维护和mysql的连接的,可以维护多个连接,每次一个请求就可以拿一个连接去发起请求

    2023-03-31 14:29:11
  • 提问者 爱吃apple的阿狸 回复 bobby #3

    但是这里代码确实是运行不了啊,直接卡住死了。我就是想了解为啥按老师视频里的demo写的,只是加了gorm的连接池,测试就通不过。。。

    2023-03-31 14:52:18
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
Go开发工程师全新版
  • 参与学习       493    人
  • 解答问题       572    个

风口上的技术,薪资水平遥遥领先,现在学习正值红利期! 未来3-5年,Go语言势必成为企业高性能项目中不可替代的语言 从基础到项目实战再到重构,对转行人员友好,真正从入门到精通!

了解课程
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师