gorm读写分离
之前同事反馈 MySQL 主库压力越来越大,虽然主从同步早就已经有了,但升级 gorm 之后一直没有引入读写分离,便商量着加入读写分离减轻主库压力。如今已上线两月有余了,比较稳定,今天来分享一下利用 gorm 实现读写分离。
gorm 的读写分离是已扩展插件的形式实现的,即 dbresolver
配置文件
db:
separation: true # 配置是否使用读写分离,方便改配置切换
master: "root:123456@tcp(127.0.0.1:3306)/a0001_chat?charset=utf8mb4&parseTime=True&loc=Local"
slave:
- "root:123456@tcp(127.0.0.1:3307)/a0001_chat?charset=utf8mb4&parseTime=True&loc=Local"
- "root:123456@tcp(127.0.0.1:3308)/a0001_chat?charset=utf8mb4&parseTime=True&loc=Local"
主要代码
package main
import (
"fmt"
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/plugin/dbresolver"
)
var (
MainDB *gorm.DB
)
func main() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %w \n", err))
}
MainDB, err = ConnectDB()
if err != nil {
panic(fmt.Errorf("Fatal MainDB config file: %w \n", err))
}
err = MainDB.Raw("select version()").Error
if err != nil {
logrus.Infof("err=%+v", err)
return
}
logrus.Info("puresai")
}
func ConnectDB() (d *gorm.DB, err error) {
if viper.GetBool("db.separation") {
return ConnectRWDB()
}
dsn := viper.GetString("db.master")
d, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
return nil, errors.Wrap(err, "数据库连接失败")
}
db, err := d.DB()
if err != nil {
return nil, errors.Wrap(err, "获取数据库实例失败")
}
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(time.Hour)
return d, nil
}
func ConnectRWDB() (d *gorm.DB, err error) {
logrus.Info("使用读写分离")
dsn := viper.GetString("db.master")
d, err = gorm.Open(mysql.New(mysql.Config{
DSN: dsn,
}))
if err != nil {
return nil, err
}
replicas := []gorm.Dialector{}
for i, s := range viper.GetStringSlice("db.slave") {
cfg := mysql.Config{
DSN: s,
}
logrus.Infof("读写分离-%d-%s", i, s)
replicas = append(replicas, mysql.New(cfg))
}
d.Use(
dbresolver.Register(dbresolver.Config{
Sources: []gorm.Dialector{mysql.New(mysql.Config{
DSN: dsn,
})},
Replicas: replicas,
Policy: dbresolver.RandomPolicy{},
}).
SetMaxIdleConns(10).
SetConnMaxLifetime(time.Hour).
SetMaxOpenConns(200),
)
return d, nil
}
主要代码就是:
d.Use(
dbresolver.Register(dbresolver.Config{
Sources: []gorm.Dialector{mysql.New(mysql.Config{
DSN: dsn,
})},
Replicas: replicas,
Policy: dbresolver.RandomPolicy{},
}),
)
more
这里只是做个demo,省略了业务代码,所以提醒一下
- 务必测试下具体业务
- 注意写后立即读的问题
- 注意db与缓存一致性问题
- 连接池和超时设置根据业务定义
参考
gorm读写分离
https://blog.puresai.com/2022/06/05/402/