【问题标题】:gorm multiple databases connection managementgorm多数据库连接管理
【发布时间】:2021-02-18 05:23:19
【问题描述】:

我有一个要求,我的应用程序可以与不同的 数据库。我如何管理 gorm 中的连接。有没有 方式 gorm 支持多个数据库的连接管理。或者我 需要创建包含所有数据库连接的映射。

if val, ok := selector.issure_db[issuer]; ok {
    return val , nil;

} else {

    var dbo  *db.DB;

    selector.mu.Lock()

    dbo, err := db.NewDb(Config)

    if err != nil {
        boot.Logger(ctx).Fatal(err.Error())
    }

    selector.issure_db[issuer] = dbo;

    selector.mu.Unlock()

    return repo ,nil;
}

有没有更好的方法来做到这一点?

【问题讨论】:

  • 在引导时创建所有数据库怎么样?
  • 我的数据库可能会根据客户而增长。
  • 你应该使用工厂函数来获取数据库,它会有互斥锁和其他东西。
  • gorm有工厂支持吗?
  • 我不知道您可以像使用选择器映射一样构建一个。

标签: go go-gorm golang-migrate


【解决方案1】:

您可以为 GORM 使用 dbresolver 插件。它管理多个源和副本,并为组维护一个底层连接池。您甚至可以使用配置将应用中的模型映射到正确的数据库。

文档中的示例:

import (
  "gorm.io/gorm"
  "gorm.io/plugin/dbresolver"
  "gorm.io/driver/mysql"
)

db, err := gorm.Open(mysql.Open("db1_dsn"), &gorm.Config{})

db.Use(dbresolver.Register(dbresolver.Config{
  // use `db2` as sources, `db3`, `db4` as replicas
  Sources:  []gorm.Dialector{mysql.Open("db2_dsn")},
  Replicas: []gorm.Dialector{mysql.Open("db3_dsn"), mysql.Open("db4_dsn")},
  // sources/replicas load balancing policy
  Policy: dbresolver.RandomPolicy{},
}).Register(dbresolver.Config{
  // use `db1` as sources (DB's default connection), `db5` as replicas for `User`, `Address`
  Replicas: []gorm.Dialector{mysql.Open("db5_dsn")},
}, &User{}, &Address{}).Register(dbresolver.Config{
  // use `db6`, `db7` as sources, `db8` as replicas for `orders`, `Product`
  Sources:  []gorm.Dialector{mysql.Open("db6_dsn"), mysql.Open("db7_dsn")},
  Replicas: []gorm.Dialector{mysql.Open("db8_dsn")},
}, "orders", &Product{}, "secondary"))

【讨论】:

    【解决方案2】:

    您可以创建一个名为 database 的包并在 init.go 文件中编写一个 init 函数,该函数可以为您拥有的每个数据库创建一个 DB 对象来连接数据库。您可以在应用程序中的任何地方使用此 db 对象,这也将启用连接池。

    init.go

    var db *gorm.DB
    
    func init() {
        var err error
        dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbPort, dbName)
        db, err = gorm.Open("mysql", dataSourceName)
        db.DB().SetConnMaxLifetime(10 * time.Second)
        db.DB().SetMaxIdleConns(10)
    
        //initialise other db objects here
    }
    
    

    users.go

    func getFirstUser() (user User) {
        db.First(&user)
        return
    }
    

    PS> 如果您必须连接到 1 或 2 个数据库,此解决方案将非常有效。如果你需要同时连接多个数据库,你应该使用 dbresolver 插件。

    旧答案

    您可以编写一个单独的函数,每次调用该函数时返回当前数据库连接对象。

    func getDBConnection(dbUser, dbPassword, dbHost, dbName string) (db *gorm.DB, err error) {
        dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", dbUser, dbPassword, dbHost, dbPort, dbName)
        db, err = gorm.Open("mysql", dataSourceName)
        db.DB().SetConnMaxLifetime(10 * time.Second)
        return
    }
    

    并在每次调用 getDBConnection 函数后调用 defer db.Close()。

    func getFirstUser() (user User) {
        db, _ := getDBConnection()
        defer db.Close()
        db.First(&user)
        return
    }
    

    这样,每次执行查询后,您的连接都会关闭。

    【讨论】:

    • 你确定这会在性能方面发挥作用吗?想象一下尝试处理例如 1000 个并发用户,并且您打开和关闭 1000 次数据库连接。
    • 是的,这里gorm.Open 不会在每次调用getDBConnection 时打开新连接。事实上,gorm.Open 内部使用了 database/sql 包来为你处理连接池。
    • 从文档中,为了让 gorm 重用连接,您需要传递现有的数据库连接 gorm.io/docs/… 而不是 DSN。在这种情况下,它如何知道它需要将其保留为连接池而不是简单的打开/关闭?
    • 感谢指正。我已经更新了我的答案,它只会创建一个 DB 对象并在整个应用程序中重用相同的 DB 对象并启用连接池。
    • 你的代码仍然没有多大意义,你有一个全局定义的 db 变量,每次你用新的连接字符串调用 init (在函数定义中丢失)如果会覆盖另一个连接让你回到第 1 格。你可以用 db 对象的映射来解决这个问题
    猜你喜欢
    • 2014-11-13
    • 2017-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-21
    • 2010-09-25
    相关资源
    最近更新 更多