调用SetPersonStatusByName函数时会话未关闭,是否遗漏关闭步骤?
问题分析与解决
你的代码存在两个关键问题,直接导致数据库会话无法正常关闭:
事务对象未被实际使用
你开启了事务tx,但后续的QueryRowContext和ExecContext都是直接调用m.db的方法,而非事务对象tx的方法。这意味着你开启的事务完全没参与实际操作,事务资源无法被正确回收,同时独立的数据库操作如果未正确处理结果,也会占用会话连接。未处理查询结果导致连接泄漏
调用QueryRowContext后,仅检查了错误但未调用Scan方法。对于sql.Row对象来说,只有调用Scan(哪怕结果为空)才会释放底层的数据库连接,否则连接会一直被占用,表现为会话未关闭。
修正后的代码
func (m *DBManager) SetPersonStatusByName(ctx context.Context, status int, name string) error { tx, err := m.db.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("begin transaction: %w", err) } defer func(tx *sql.Tx) { rErr := tx.Rollback() if rErr != nil && !errors.Is(rErr, sql.ErrTxDone) { err = errors.Join(err, rErr) } }(tx) const selectForUpdate = `select status from persons where name=$1 for update;` // 使用tx执行查询,并且调用Scan释放连接 var currentStatus int if err = tx.QueryRowContext(ctx, selectForUpdate, name).Scan(¤tStatus); err != nil { return fmt.Errorf("select for update: %w", err) } const updateStatus = `update persons set status=$1 where name=$2;` // 使用tx执行更新操作 if _, err = tx.ExecContext(ctx, updateStatus, status, name); err != nil { return fmt.Errorf("update row: %w", err) } err = tx.Commit() if err != nil { return fmt.Errorf("commit transaction: %w", err) } return nil }
关键修改点说明
- 将所有数据库操作从
m.db改为tx调用,确保所有操作都在事务上下文内执行,事务提交或回滚后能正确释放资源。 - 对
QueryRowContext的结果调用Scan方法,即使你不需要当前状态的值,也可以用_接收(比如Scan(&_)),确保底层连接被释放。
内容的提问来源于stack exchange,提问作者vovanchello
相关产品推荐
相关产品推荐

