Golang-Postgres ..关闭数据库连接不适用于特定查询

时间:2016-06-29 09:27:09

标签: database postgresql go postgresql-9.4

我正在使用golang访问postgresql。 这是我的功能

for {
    db, err := database.GetNewConnection(dbname)

    err = db.QueryRow("SELECT COALESCE(COUNT(1),0) FROM table").Scan(&count)

    if count == 0 {

        var insert = "INSERT INTO table(last_update_time,next_update_time,schedule_frequency)" +
            "VALUES($1,$2,$3)"
        prep_ins, err := db.Prepare(insert)
        if err != nil {
            return
        }
        _, err = prep_ins.Exec(cur_time, 1464718530, 86400)
        if err != nil {
            return
        }
        defer prep_ins.Close()
        defer db.Close()
    } else {
        var sel_str = "SELECT next_update_time FROM table"
        prep_update, err := db.Prepare(sel_str)
        if err != nil {

            return
        }
        _, err = prep_update.Exec()
        if err != nil {
            defer prep_update.Close()
            return
        }
        defer prep_update.Close()
        defer db.Close()
    }
    time.Sleep(10 * 60 * time.Second)
}

每10分钟,此函数将运行并执行这些语句。它工作正常,但连接将处于空闲状态。我正在使用pg_stat_activity进行检查,状态仅为空闲状态。每隔10分钟,就会创建一个新的连接并进入空闲状态。所以它会不断增加。我不知道为什么会这样。

提前致谢。

3 个答案:

答案 0 :(得分:3)

for循环是一个无限循环,它永远不会返回。这意味着永远不会执行延期,也不会关闭连接。您需要在循环结束时添加显式db.Close()

如果每次执行任务时都不需要新连接,也可以将db的初始化移出循环。您只需创建一个连接并重复使用即可。如果您这样做,请在defer的初始化下方移动db语句。如果数据库操作返回一些错误,则延迟将关闭连接。

db, err := database.GetNewConnection(dbname)
if err != nil {
    return fmt.Errorf("Error in creating database connection: %+v", err)
}
defer db.Close()
for {

    // ...

    db.Close()
}

通过执行此操作,您可以确保在函数的每个返回路径上关闭连接(https://blog.golang.org/defer-panic-and-recover

如果您仍然在循环中保留db初始化,请删除defers,因为他们只会在显式关闭数据库连接时添加到延迟堆栈。

答案 1 :(得分:1)

正如@abhink所指出的那样,由于永远运行for循环,因此未调用defer语句。使用for循环计划执行一段代码的实现并不是最佳的。

您应该使用golang频道。

package main

import (
    "fmt"
    "time"
)

func main() {
    //You can configure your tick for 10 mnts.
    tick := time.Tick(100 * time.Millisecond)
    //some exit condition
    exit := time.After(1000 * time.Millisecond)
    for {
        select {
        case <-tick:
            poke()
        case <-exit:
            fmt.Println("Exit")
            return
        default:
            fmt.Println("    .")
            time.Sleep(50 * time.Millisecond)
        }
    }
}

func poke() {
    fmt.Println("Opening connection")
    defer fmt.Println("Closing connection")
    fmt.Println("Inserting into Database")
}

您可以更改pock()功能以将记录插入数据库。并且您可以配置自动收报机每10分钟打勾。由于每个tick都会触发一个新的函数调用,因此一旦pock()函数执行完成,就会调用defer。

请参阅此操场代码段https://play.golang.org/p/1fQgbmI9LY

答案 2 :(得分:1)

defer函数仅在函数返回时调用。在循环中,当循环处于活动状态时,defer db.Close()永远不会执行,因此函数永远不会被调用。

您可以将for中的逻辑包装在闭包中以实现延迟:

for{
   func(){
       // inner code 
   }()
   time.Sleep(10 * 60 * time.Second)
}