mysql资源池死锁问题

    周末遇到一个服务卡死的问题,经过日志分析发现,可能锁死在两处数据库操作里面了,查看了rds数据库服务端慢日志,又无所获。当时,觉得可能存在的问题就是在一个数据库的事务操作流程里面,又执行了一个另外一个表的数据库写操作,因为,不知道确切原因,所以,先把这个数据库写操作移到了事务操作的外面,那么后面问题没有发生。

    周一上班,再进一步和同事讨论分析这个问题,发现果然是mysql事务里面执行的这条数据库写操作造成的资源死锁,我又踩坑了。

问题代码如下:

		//query and lock line  拿锁
		if err := tx.Set("gorm:query_option", "FOR UPDATE").Take(&xxx, xxx).Error; err != nil {
			return err
		}
		
		// 事务操作Update
		tx.Model(&xxx).Update("game_duration", xxx)
		
		
		// 另外一个数据库操作,需要拿连接,以前就有的函数,直接拿来复用
		if e := yyy(); e != nil {
		}
		
		// 事务操作Commit
		if err := tx.Commit().Error; err != nil {
			return err
		}	

看出来了吗???

mysql事务本身会获取一个数据库可用连接来执行事务操作,这个连接在事务Commit以后释放;而Commit前面的一个数据库操作本身也要获取一个连接来执行里面的exec操作;当有大量类似流程的事务上来以后,那么就造成了,占着事务连接本身,在释放事务连接之前,如果拿不到yyy这个数据库操作所需要的连接,就会一直卡在yyy里面等待,本身占用的事务连接也不会释放,从而构造出了资源死锁的局面!!!拿着资源又去申请资源这种场景万万不能用于mysql数据库事务操作的场景,这个坑还是比较隐蔽~~

Commit后再去执行其他数据库操作,获取连接就没问题了,Commit过程中会释放当前事务连接。

func (tx *Tx) Commit() error {
	// Check context first to avoid transaction leak.
	// If put it behind tx.done CompareAndSwap statement, we can't ensure
	// the consistency between tx.done and the real COMMIT operation.
	select {
	default:
	case <-tx.ctx.Done():
		if atomic.LoadInt32(&tx.done) == 1 {
			return ErrTxDone
		}
		return tx.ctx.Err()
	}
	if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
		return ErrTxDone
	}
	var err error
	withLock(tx.dc, func() {
		err = tx.txi.Commit()
	})
	if err != driver.ErrBadConn {
		tx.closePrepared()
	}
	tx.close(err)
	return err
}
// close returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit.
func (tx *Tx) close(err error) {
	tx.cancel()

	tx.closemu.Lock()
	defer tx.closemu.Unlock()

	tx.releaseConn(err)
	tx.dc = nil
	tx.txi = nil
}

后人观之,鉴之,而不犯之。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值