【问题标题】:How to reuse a single Postgres DB connection for row inserts in Go?如何在 Go 中为行插入重用单个 Postgres DB 连接?
【发布时间】:2018-05-22 05:09:18
【问题描述】:

我正在尝试使用 Go 为从 rabbitmq 接收到的每条新消息插入一行数据到 Postgres 表中,使用到在下面代码的 init 函数中打开的 DB 的单个连接。

代码不是只打开一个连接,而是打开 497 并最大化导致行插入停止...

我尝试使用opening and closing DB connection in Go appopen database connection inside a function这些问题中的信息,说我应该打开一个连接并使用全局数据库来允许主函数将sql语句传递给在init函数中打开的连接。

我以为我已经这样做了,但是正在为每个新行打开一个新连接,因此一旦达到 postgres 连接限制,代码就会停止工作......

我是 Go 新手,编程经验有限,过去两天我一直在尝试理解/解决这个问题,我真的可以通过一些帮助来了解我哪里出了问题......

var db *sql.DB

func init() {
    var err error
    db, err = sql.Open ( "postgres", "postgres://postgres:postgres@SERVER/PORT/DB")
    if err != nil {
        log.Fatal("Invalid DB config:", err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal("DB unreachable:", err)
    }
}

func main() {

// RABBITMQ CONNECTION CODE IS HERE

// EACH MESSAGE RECEIVED IS SPLIT TO LEGEND, STATUS, TIMESTAMP VARIABLES

// VARIABLES ARE PASSED TO sqlSatement    

        sqlStatement := `
        INSERT INTO heartbeat ("Legend", "Status", "TimeStamp")
        VALUES ($1, $2, $3)
`
        // sqlStatement IS THEN PASSED TO db.QueryRow

        db.QueryRow(sqlStatement, Legend, Status, TimeStamp)
    }
}()

<-forever
}

完整代码如下:

package main

import (
    "database/sql"
    "log"
    _ "github.com/lib/pq"

    "github.com/streadway/amqp"
    "strings"
)
var db *sql.DB

func failOnError(err error, msg string) {
    if err != nil {
        log.Fatalf("%s: %s", msg, err)
    }
}

func init() {
    var err error
    db, err = sql.Open ( "postgres", "postgres://postgres:postgres@192.168.1.69:5432/test?sslmode=disable")
    if err != nil {
        log.Fatal("Invalid DB config:", err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal("DB unreachable:", err)
    }
}

func main() {
    conn, err := amqp.Dial("amqp://Admin:Admin@192.168.1.69:50003/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "HEARTBEAT", // name
        false,       // durable
        false,       // delete when unused
        false,       // exclusive
        false,       // no-wait
        nil,         // arguments
    )
    failOnError(err, "Failed to declare a queue")

    msgs, err := ch.Consume(
        q.Name, // queue
        "",     // consumer
        false,  // auto-ack
        false,  // exclusive
        false,  // no-local
        false,  // no-wait
        nil,    // args
    )
    failOnError(err, "Failed to register a consumer")

    forever := make(chan bool)

    go func() {

        for d := range msgs {
            myString := string(d.Body[:])
            result := strings.Split(myString, ",")
            Legend := result[0]
            Status := result[1]
            TimeStamp := result[2]

            sqlStatement := `
    INSERT INTO heartbeat ("Legend", "Status", "TimeStamp")
    VALUES ($1, $2, $3)
    `
            //
            db.QueryRow(sqlStatement, Legend, Status, TimeStamp)
        }
    }()

    <-forever
}

【问题讨论】:

  • 提供的代码不应为每个查询创建连接。但您可以只创建一次语句。
  • @zerkms 这里已经很晚了,我不太明白你在说什么,但我会在早上睡觉后再次检查,因为我的大脑被炸了 - 你的意思是我需要把语句移到init函数???原谅我的新鲜感并感谢您的回复...
  • 您不需要 这样做,这只是一种改进。但我的主要观点是,我不相信您提供的代码摘录可能会导致您观察到的症状。
  • @zerkms,好的,谢谢,这就是我拔头发的原因,我试图了解我哪里出错了,因为代码总是在 497 行之后停止插入,如果我检查了使用 pgadmin 连接到 postgres 我看到有 497 个!
  • @MarkSmith 显示更多代码。问题在于省略的代码。

标签: go


【解决方案1】:

首先,*sql.DB 不是一个连接,而是一个连接池,它会打开尽可能多的连接,只要它需要和尽可能多的 postgres 服务器允许。只有当池中没有空闲连接可供使用时,它才会打开新连接。


所以问题是DB打开的连接没有被释放,为什么?因为您使用的是QueryRow 而没有在返回的*Row 值上调用Scan

在后台*Row 拥有一个*Rows 实例,该实例可以访问自己的连接,并且在调用Scan 时该连接会自动释放。如果未调用Scan,则不会释放连接,这会导致DB 池在下一次调用QueryRow 时打开一个新连接。因此,由于您没有释放任何连接,DB 会不断打开新连接,直到达到 postgres 设置指定的限制,然后对 QueryRow 的下一次调用挂起,因为它等待连接变为空闲。

因此,如果您不关心输出,则需要使用Exec,或者需要在返回的*Row 上调用Scan

【讨论】:

  • 感谢您清晰详细的解释,这很有道理,对我的理解很有帮助!
  • 我已经能够使用“扫描”解决问题,并且代码现在可以按预期工作,谢谢!
  • hastebin.com/tokatazihi.cs 这也会导致问题吗? @mkopriva
  • @AniruddhaChakraborty 很抱歉,但我不知道,因为该代码正在使用 gorm,而且我对 gorm 没有足够的了解,无法知道它是否会导致问题。跨度>
猜你喜欢
  • 1970-01-01
  • 2022-12-17
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-26
  • 2021-11-11
  • 1970-01-01
相关资源
最近更新 更多