You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GORM Postgres事务中并行查询出现driver: bad connection问题求助

在GORM事务中并行执行查询出现「driver: bad connection」错误的问题排查与解决

问题描述

我在使用GORM操作PostgreSQL时遇到一个问题:当在事务中并行执行多条查询时,会抛出「driver: bad connection」错误。如果注释掉事务开启(tx := db.Begin())和提交(tx.Commit())的相关代码,这个问题就不会复现。我不清楚问题原因,希望得到分析和解决建议。

复现代码

package main

import (
    "github.com/avast/retry-go"
    "github.com/sirupsen/logrus"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
    "os"
    "strconv"
    "time"
)

var (
    db *gorm.DB
)

func NewGorm(databaseURL string) (*gorm.DB, error) {
    db, err := gorm.Open(
        postgres.New(
            postgres.Config{
                DSN:                  databaseURL,
                PreferSimpleProtocol: true, // disables implicit prepared statement usage
            },
        ),
        &gorm.Config{
            Logger: logger.Default.LogMode(logger.Silent),
        },
    )

    if err != nil {
        return nil, err
    }

    return db, nil
}

func Init() {
    databaseURL := os.Getenv("DATABASE_URL")

    logrus.WithField("databaseURL", databaseURL).Info("Database connection")

    if err := retry.Do(
        func() error {
            var connectionErr error

            db, connectionErr = NewGorm(databaseURL)

            if connectionErr != nil {
                logrus.Warning("retrying database")

                return connectionErr
            }

            return nil
        },
    ); err != nil {
        logrus.WithError(err).Panic("database")
    }

    if debug, err := strconv.ParseBool(os.Getenv("DEBUG")); debug && err == nil {
        db = db.Debug()
    } else {
        logrus.Warning(err)
    }
}

func select_from_db(tx *gorm.DB) {
    tx.Table("table_name").Find(&[]models.A{})
    tx.Table("table_name").Find(&[]models.A{})
    tx.Table("table_name").Find(&[]models.A{})
    tx.Table("table_name").Find(&[]models.A{})
}

func main() {
    logrus.SetLevel(logrus.TraceLevel)
    Init()
    tx := db.Begin()
    go select_from_db(tx)
    go select_from_db(tx)
    go select_from_db(tx)
    go select_from_db(tx)
    go select_from_db(tx)
    time.Sleep(3 * time.Second)
    tx.Commit()
}

运行错误信息

[0.222ms] [rows:0] SELECT * FROM "table_name"

2023/06/15 13:14:51 main.go:72 driver: bad connection

问题原因分析

  • GORM的事务对象(*gorm.DB)不是线程安全的:事务绑定了单个数据库连接,多个goroutine并行操作时会同时读写这个连接的状态,导致连接内部逻辑混乱,最终触发「bad connection」错误。
  • 非事务场景下,GORM会自动从连接池为每个操作分配独立连接,每个goroutine使用各自的连接,不会出现竞争冲突;但事务场景下所有操作复用同一个连接,并行操作必然导致资源竞争。

解决办法

方案1:事务内操作改为串行执行

如果业务允许,将并行调用改为串行,确保同一事务内的操作按顺序执行,避免连接竞争:

func main() {
    logrus.SetLevel(logrus.TraceLevel)
    Init()
    tx := db.Begin()
    select_from_db(tx)
    select_from_db(tx)
    select_from_db(tx)
    select_from_db(tx)
    select_from_db(tx)
    tx.Commit()
}

方案2:为每个goroutine创建独立事务

如果必须并行处理,让每个goroutine开启自己的独立事务,操作完成后各自提交(注意这种方式下多个事务是独立的,无法保证全局原子性):

func select_from_db(db *gorm.DB) {
    tx := db.Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        } else {
            tx.Commit()
        }
    }()
    tx.Table("table_name").Find(&[]models.A{})
    tx.Table("table_name").Find(&[]models.A{})
    tx.Table("table_name").Find(&[]models.A{})
    tx.Table("table_name").Find(&[]models.A{})
}

func main() {
    logrus.SetLevel(logrus.TraceLevel)
    Init()
    go select_from_db(db)
    go select_from_db(db)
    go select_from_db(db)
    go select_from_db(db)
    go select_from_db(db)
    time.Sleep(3 * time.Second)
}

方案3:放弃事务,使用连接池处理并行操作

如果业务不需要事务的原子性保证,直接使用全局db对象执行查询,GORM会自动从连接池为每个goroutine分配独立连接,避免竞争:

func select_from_db(db *gorm.DB) {
    db.Table("table_name").Find(&[]models.A{})
    db.Table("table_name").Find(&[]models.A{})
    db.Table("table_name").Find(&[]models.A{})
    db.Table("table_name").Find(&[]models.A{})
}

func main() {
    logrus.SetLevel(logrus.TraceLevel)
    Init()
    go select_from_db(db)
    go select_from_db(db)
    go select_from_db(db)
    go select_from_db(db)
    go select_from_db(db)
    time.Sleep(3 * time.Second)
}

内容的提问来源于stack exchange,提问作者Omri. B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:14:55