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"

](github.13sai.com/2022/06/05/402/#%... “主要代码”)主要代码

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("13sai")
}

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与缓存一致性问题
  • 连接池和超时设置根据业务定义

参考

本作品采用《CC 协议》,转载必须注明作者和本文链接
分享开发知识,欢迎交流。公众号:开源到
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!