登录
首页 >  Golang >  Go问答

如何在 Go 中为行插入重用单个 Postgres DB 连接?

来源:Golang技术栈

时间:2023-04-05 07:18:02 422浏览 收藏

一分耕耘,一分收获!既然打开了这篇文章《如何在 Go 中为行插入重用单个 Postgres DB 连接?》,就坚持看下去吧!文中内容包含golang等等知识点...希望你能在阅读本文后,能真真实实学到知识或者帮你解决心中的疑惑,也欢迎大佬或者新人朋友们多留言评论,多给建议!谢谢!

问题内容

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

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

我已经尝试使用这些问题中的信息[在 Go 应用程序中打开和关闭数据库连接](https://stackoverflow.com/questions/40587008/how-do-i-handle- opening-closing-db-connection-in-a-go- app/40587071#40587071)并[在一个函数](https://stackoverflow.com/questions/42581697/referring- to-an-open-database-connection-inside-a-function- golang)中打开数据库连接,该函数说我应该打开一个连接并使用全局数据库来允许主函数将 sql 语句传递给打开的连接初始化函数。

我以为我已经这样做了,但是正在为每个新行打开一个新连接,因此一旦达到 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)
    }
}()


完整代码如下所示:

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)
        }
    }()

    

正确答案

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


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

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

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

好了,本文到此结束,带大家了解了《如何在 Go 中为行插入重用单个 Postgres DB 连接?》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!

声明:本文转载于:Golang技术栈 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表