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

时间:2017-12-08 00:50:27

标签: go

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

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

我尝试使用这些问题opening and closing DB connection in Go appopen database connection inside a function中的信息,它们说我应该打开一个连接并使用全局db来允许main函数将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)
    }
}()

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

1 个答案:

答案 0 :(得分:11)

首先,*sql.DB不是连接而是连接池,它将打开尽可能多的连接以及postgres服务器允许的连接数。它只在池中没有空闲的连接准备好使用时才打开新连接。

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

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

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

相关问题