postgresql - 为什么数据库连接会自动关闭?

标签 postgresql go go-gorm

我在使用 Gorm/Psql 时遇到问题,我的数据库连接会自动关闭。

我从不在 main.go 中调用 defer dbInstance.Close()(现在不再调用了,我已经删除了它,因为这是我的代码中唯一我觉得可以连接的地方错误关闭)也从未在其他任何地方。

我初始化数据库的方式是使用如下所示的“db”包:

package db

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"
)

var DbInstance *gorm.DB

func Init() *gorm.DB {
    if DbInstance != nil {
        return DbInstance
    }
    fmt.Println("Opening Db")
    db, err := gorm.Open("postgres", "host=localhost port=5432 user=admin dbname=dbname sslmode=disable password=")
    if err != nil {
        fmt.Println(err)
        panic("failed to connect database")
    }
    return db
}

然后我在我的 main.go 中调用 db.Init(),然后只从我程序的其余部分调用来自“db.dbInstance”的数据库。

正如我之前提到的,我曾经从 main.go 中调用 defer db.DbInstance.Close() 但我尝试删除它以查看它是否解决了问题,但它没有。

奇怪的是,数据库连接将在许多不同的调用/函数中工作数小时,但总是在某个时刻结束。

据我了解it should work :

gorm.Open() uses https://golang.org/pkg/database/sql/, which is threadsafe and handles connection pooling for you. Don't call gorm.Open() every request. Call it once when setting up your application, and make sure you use defer db.Close() so connections are cleanly ended when your program exits.

最后我需要补充一点,它似乎(我不是 100% 确定)在我进行批量插入后它正在关闭,但是 .Close() 函数从未被调用,我程序中的任何位置

我对可能发生的事情有点迷茫?垃圾收集器(var 是全局的没有意义)? psql 驱动程序在后台关闭?配置问题?

为了以防万一,我添加了批处理功能以供引用:

func InsertWithPostGresLimitSizeV2(DB *gorm.DB, array []interface{}) {
    if len(array) == 0 {
        return
    }
    numberOfParams := len(DB.NewScope(array[0]).Fields())
    // postgres is limited to 65535 params.
    maxStructPerBulk := int(65535 / numberOfParams)
    currentIndex := 0
    if len(array) > maxStructPerBulk {
        for len(array) > currentIndex {
            if (maxStructPerBulk + currentIndex) < len(array) {
                slice := array[currentIndex:(currentIndex + maxStructPerBulk)]
                currentIndex += maxStructPerBulk
                _, err := DB.BatchInsert(slice)
                log.Println(err)
            } else {

                slice := array[currentIndex:len(array)]
                currentIndex = len(array)
                _, err := DB.BatchInsert(slice)
                log.Println(err)
            }
        }
    } else {
        _, err := DB.BatchInsert(array)
        log.Println(err)
    }
}

func BatchInsert(db *gorm.DB,objArr []interface{}) (int64, error) {
    if len(objArr) == 0 {
        return 0, errors.New("insert a slice length of 0")
    }

    mainObj := objArr[0]
    mainScope := db.NewScope(mainObj)
    mainFields := mainScope.Fields()
    quoted := make([]string, 0, len(mainFields))
    for i := range mainFields {
        if (mainFields[i].IsPrimaryKey && mainFields[i].IsBlank) || (mainFields[i].IsIgnored) {
            continue
        }
        quoted = append(quoted, mainScope.Quote(mainFields[i].DBName))
    }

    placeholdersArr := make([]string, 0, len(objArr))

    for _, obj := range objArr {
        scope := db.NewScope(obj)
        fields := scope.Fields()
        placeholders := make([]string, 0, len(fields))
        for i := range fields {
            if (fields[i].IsPrimaryKey && fields[i].IsBlank) || (fields[i].IsIgnored) {
                continue
            }
            var vars interface{}
            if (fields[i].Name == "CreatedAt" || fields[i].Name == "UpdatedAt") && fields[i].IsBlank {
                vars = gorm.NowFunc()
            } else {
                vars = fields[i].Field.Interface()
            }
            placeholders = append(placeholders, mainScope.AddToVars(vars))
        }
        placeholdersStr := "(" + strings.Join(placeholders, ", ") + ")"
        placeholdersArr = append(placeholdersArr, placeholdersStr)

        mainScope.SQLVars = append(mainScope.SQLVars, scope.SQLVars...)
    }
    mainScope.Raw(fmt.Sprintf("INSERT INTO %s (%s) VALUES %s",
        mainScope.QuotedTableName(),
        strings.Join(quoted, ", "),
        strings.Join(placeholdersArr, ", "),
    ))
    if err := mainScope.Exec().DB().Error; err != nil {
        return 0, err
    }
    return mainScope.DB().RowsAffected, nil
}

最后一件事是,我正在考虑通过调用我的数据库来“解决”这个问题,但是 ping 会减慢我的每个调用:

func getDb() *gorm.DB {
    err := DbInstance.DB().Ping()
    if err != nil {
        fmt.Println("Connection to db closed opening a new one")
        return Init()
    }
    return DbInstance
}

最佳答案

您可以全局搜索 DbInstance.Close() 以确保永远不会调用它来自行关闭它。 如果不是,您可以将数据库超时设置得更长,并增加空闲数据库连接的数量。

最后,最重要的是支持自动重连db dataSource。

这是我的自动重新连接部分的一部分,您可能会引用:

var DB *gorm.DB
func init() {
    dbConfig = fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s password=%s",
        "localhost",
        "postgres",
        "dbname",
        "disable",
        "password",
    )
    db, err := gorm.Open("postgres",
        dbConfig,
    )
    db.SingularTable(true)
    db.LogMode(true)
    db.DB().SetConnMaxLifetime(10 * time.Second)
    db.DB().SetMaxIdleConns(30)
    DB = db
    // auto-connect,ping per 60s, re-connect on fail or error with intervels 3s, 3s, 15s, 30s, 60s, 60s ...
    go func(dbConfig string) {
        var intervals = []time.Duration{3 * time.Second, 3 * time.Second, 15 * time.Second, 30 * time.Second, 60 * time.Second,
        }
        for {
            time.Sleep(60 * time.Second)
            if e := DB.DB().Ping(); e != nil {
            L:
                for i := 0; i < len(intervals); i++ {
                    e2 := RetryHandler(3, func() (bool, error) {
                        var e error
                        DB, e = gorm.Open("postgres", dbConfig)
                        if e != nil {
                            return false, errorx.Wrap(e)
                        }
                        return true, nil
                    })
                    if e2 != nil {
                        fmt.Println(e.Error())
                        time.Sleep(intervals[i])
                        if i == len(intervals)-1 {
                            i--
                        }
                        continue
                    }
                    break L
                }

            }
        }
    }(dbConfig)
}

顺便说一句:

// Try f() n times on fail and one time on success 
func RetryHandler(n int, f func() (bool, error)) error {
    ok, er := f()
    if ok && er == nil {
        return nil
    }
    if n-1 > 0 {
        return RetryHandler(n-1, f)
    }
    return er
}

关于postgresql - 为什么数据库连接会自动关闭?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57987883/

相关文章:

postgresql - 双左连接忽略过滤器

postgresql - 函数或过程名称的别名

mongodb - golang mongoDB 唯一索引在多个键时不起作用

mysql - 删除无效

go - 获取对象返回空字符串作为值Golang

sql - Hypertable 与 HBase 以及 BigTable 与 SQL

django - Docker 的 postgres 容器的密码验证失败

google-app-engine - Google appengine 数据存储过滤器与 OR like sql (golang)

Go defer - 循环打开和复制文件

go - 使用 Gorm 将值从一列映射到单独表中的另一列