之前同事反馈 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与缓存一致性问题
- 连接池和超时设置根据业务定义
参考