DBResolver
DBResolver 为 GORM 添加了多数据库支持,支持以下功能:
- 多个源数据库和副本数据库
- 读写分离
- 基于工作表/结构体的自动连接切换
- 手动连接切换
- 源数据库/副本数据库负载均衡
- 支持原生 SQL
- 事务处理
快速开始
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{
// 使用 `db2` 作为源数据库,`db3`、`db4` 作为副本数据库
Sources: []gorm.Dialector{mysql.Open("db2_dsn")},
Replicas: []gorm.Dialector{mysql.Open("db3_dsn"), mysql.Open("db4_dsn")},
// 源数据库/副本数据库负载均衡策略
Policy: dbresolver.RandomPolicy{},
// 在日志中打印源数据库/副本数据库模式
ResolverModeReplica: true,
}).Register(dbresolver.Config{
// 对于 `User` 和 `Address`,使用 `db1` 作为源数据库(DB 的默认连接),`db5` 作为副本数据库
Replicas: []gorm.Dialector{mysql.Open("db5_dsn")},
}, &User{}, &Address{}).Register(dbresolver.Config{
// 对于 `orders`、`Product` 和 "secondary",使用 `db6`、`db7` 作为源数据库,`db8` 作为副本数据库
Sources: []gorm.Dialector{mysql.Open("db6_dsn"), mysql.Open("db7_dsn")},
Replicas: []gorm.Dialector{mysql.Open("db8_dsn")},
}, "orders", &Product{}, "secondary"))
自动连接切换
DBResolver 将根据正在操作的表/结构体自动切换连接。
对于原生 SQL,DBResolver 将从 SQL 中提取表名以匹配解析器,除非 SQL 以 SELECT
开头,否则将使用 sources
,例如:
// `User` 解析器示例
DB.Table("users").Rows() // 副本数据库 `db5`
DB.Model(&User{}).Find(&AdvancedUser{}) // 副本数据库 `db5`
DB.Exec("update users set name = ?", "jinzhu") // 源数据库 `db1`
DB.Raw("select name from users").Row().Scan(&name) // 副本数据库 `db5`
DB.Create(&user) // 源数据库 `db1`
DB.Delete(&User{}, "name = ?", "jinzhu") // 源数据库 `db1`
DB.Table("users").Update("name", "jinzhu") // 源数据库 `db1`
// 全局解析器示例
DB.Find(&Pet{}) // 副本数据库 `db3`/`db4`
DB.Save(&Pet{}) // 源数据库 `db2`
// Orders 解析器示例
DB.Find(&Order{}) // 副本数据库 `db8`
DB.Table("orders").Find(&Report{}) // 副本数据库 `db8`
读写分离
DBResolver 基于当前使用的 GORM 回调 实现读写分离。
对于 Query
、Row
回调,除非指定 Write
模式,否则将使用 replicas
。
对于 Raw
回调,如果 SQL 以 SELECT
开头,则语句被视为只读,将使用 replicas
。
手动连接切换
// 使用写模式:从源数据库 `db1` 读取用户
DB.Clauses(dbresolver.Write).First(&user)
// 指定解析器:从 `secondary` 的副本数据库 db8 读取用户
DB.Clauses(dbresolver.Use("secondary")).First(&user)
// 指定解析器和写模式:从 `secondary` 的源数据库 db6 或 db7 读取用户
DB.Clauses(dbresolver.Use("secondary"), dbresolver.Write).First(&user)
事务
在使用事务时,DBResolver 将继续使用事务,不会根据配置切换到源数据库/副本数据库。
但你可以在开始事务之前指定要使用的数据库,例如:
// 基于默认副本数据库开始事务
tx := DB.Clauses(dbresolver.Read).Begin()
// 基于默认源数据库开始事务
tx := DB.Clauses(dbresolver.Write).Begin()
// 基于 `secondary` 的源数据库开始事务
tx := DB.Clauses(dbresolver.Use("secondary"), dbresolver.Write).Begin()
负载均衡
GORM 支持基于策略的源数据库/副本数据库负载均衡,策略是一个实现以下接口的接口:
type Policy interface {
Resolve([]gorm.ConnPool) gorm.ConnPool
}
目前只实现了 RandomPolicy
,如果未指定策略,它是默认选项。
连接池
DB.Use(
dbresolver.Register(dbresolver.Config{ /* xxx */ }).
SetConnMaxIdleTime(time.Hour).
SetConnMaxLifetime(24 * time.Hour).
SetMaxIdleConns(100).
SetMaxOpenConns(200)
)