当前位置: 首页 > news >正文

GO语言使用gorm的dbresolver插件实现数据库读写分离

GO语言使用gorm的dbresolver插件实现数据库读写分离

settings.yaml文件中读库跟写库的配置信息:db:读库,db1:写库

db:user: rootpassword: 123456host: 127.0.0.1port: 3306db: blogxdebug: falsesource: mysql
db1:user: rootpassword: 123456host: 127.0.0.1port: 3306db: testdebug: falsesource: mysql

conf/enter.go

type Config struct {System System `yaml:"system"`Log    Log    `yaml:"log"`DB     DB     `yaml:"db"`  //读库DB1    DB     `yaml:"db1"` //写库
}

conf/conf_db.go

type DB struct {User     string `yaml:"user"`Password string `yaml:"password"`Host     string `yaml:"host"`Port     int    `yaml:"port"`DB       string `yaml:"db"`Debug    bool   `yaml:"debug"`  //打印全部日志Source   string `yaml:"source"` //数据库的源 MySQL pgsql
}func (d DB) DSN() string {return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local",d.User, d.Password, d.Host, d.Port, d.DB)
}
func (d DB) Empty() bool {return d.User == "" && d.Password == "" && d.Host == "" && d.Port == 0
}

core/init_db.go: 利用 GORM 的 dbresolver 插件,把两份配置注册成 “Sources / Replicas”

import ("blogx_server/global""github.com/sirupsen/logrus""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/plugin/dbresolver""time"
)func InitDB() *gorm.DB {dc := global.Config.DB   //读库dc1 := global.Config.DB1 //写库db, err := gorm.Open(mysql.Open(dc.DSN()), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true, // 表迁移时不自动创建外键约束})if err != nil {panic(err)}sqlDB, err := db.DB()sqlDB.SetMaxIdleConns(10)           // 空闲连接池大小sqlDB.SetMaxOpenConns(100)          // 最大打开连接数sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大存活时间logrus.Info("数据库连接成功")if !dc1.Empty() {//读写库不为空,就注册读写分离的配置err = db.Use(dbresolver.Register(dbresolver.Config{Sources:  []gorm.Dialector{mysql.Open(dc1.DSN())}, //写Replicas: []gorm.Dialector{mysql.Open(dc.DSN())},  //读Policy: dbresolver.RandomPolicy{},}))if err != nil {logrus.Fatalf("读写配置错误:%s", err)}}return db
}

项目的“数据库读写分离”功能依赖 3 处代码:

  1. 配置层
    settings.yaml 里的 db:(读库)和 db1:(写库)
    conf/conf_db.go 里的 DB 结构体 + Empty() 方法
  2. 启动层
    core/init_db.go 中利用 GORM 的 dbresolver 插件,把两份配置注册成 “Sources / Replicas”
  3. 业务层
    • 业务代码照常用 db.Create / db.Find…,GORM 自动把写请求导向写库,把读请求导向读库。

下面循序渐进地把每一步拆开讲。

──────────────────────────────────────
一、YAML + 结构体:怎样描述两套数据库

  1. settings.yaml(缩写示例)
db:          # 读库user: rootpassword: 123456host: 127.0.0.1port: 3306db: blogxdebug: falsesource: mysqldb1:         # 写库user: rootpassword: 123456host: 192.168.1.10port: 3306db: blogxdebug: falsesource: mysql
  1. conf/enter.go
type Config struct {System System `yaml:"system"`Log    Log    `yaml:"log"`DB     DB     `yaml:"db"`   // 读DB1    DB     `yaml:"db1"`  // 写
}
  1. conf/conf_db.go
    DB.DSN() 把账号/主机/库名拼成 MySQL 连接串;
    DB.Empty() 判断该配置是否完全为空(四项都没填);后面用于“若用户没配写库就别启动读写分离”。

──────────────────────────────────────
二、InitDB:用 dbresolver 把“读 / 写”连起来

核心流程位于 core/init_db.go

dc  := global.Config.DB   // 读库
dc1 := global.Config.DB1  // 写库
db, _ := gorm.Open(mysql.Open(dc.DSN()), &gorm.Config{...}) // 先连读库
...
if !dc1.Empty() {         // 写库不为空才开启读写分离err = db.Use(dbresolver.Register(dbresolver.Config{Sources:  []gorm.Dialector{mysql.Open(dc1.DSN())}, // 写Replicas: []gorm.Dialector{mysql.Open(dc.DSN())},  // 读Policy:   dbresolver.RandomPolicy{},               // 读负载均衡策略}))...
}

细节说明:

  1. 先用读库 DSN 建立基础连接——这样即便只配一套库,也能正常跑起来。

  2. 判断写库是否留空——dc1.Empty() 会返回 true/false:
    • 如果用户没有在 YAML 里填 db1:,代码就不会注册 dbresolver,整个项目退化成“单库”模式。
    • 如果配了,才执行 db.Use(...),将两套连接池交给插件管理。

  3. Sources / Replicas 的含义
    Sources:主库(写库)。GORM 会把所有会写入数据的操作(Create、Update、Delete、Exec、事务)发送到这里。
    Replicas:从库(读库)。所有纯查询(First、Find、Scan、Raw SQL 查询)会走这里。

  4. Policy:当有多台从库时负责负载均衡。此处用 RandomPolicy,即随机挑一台;GORM 还内置 RoundRobinPolicy 等策略。

  5. 错误处理:如果 db.Use 返回 err,立即 logrus.Fatalf(...) 终止启动,避免带错配置上线。

──────────────────────────────────────
三、插件工作原理(你不需要额外代码)

dbresolver 在内部注册了一个 callbacks 链。当你后续写下

db.First(&user, 1)          // 纯查询
db.Create(&order)           // 写入

它会自动根据 SQL 操作类别,把请求路由到:
Replicas(读池) → user 查询
Sources(写池) → order 插入

事务(db.Transaction(func(tx *gorm.DB) {...}))默认锁定在 Sources,保证读写一致性。

──────────────────────────────────────
四、连接池参数与日志

连接池大小、生命周期在 3 行里统一设置(对读库池生效;写库池由 dbresolver 使用默认值,也可再通过 Configure 调整):

sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)

成功后 logrus.Info("数据库连接成功") 会在控制台彩显,并写到每日分割的文件里。

──────────────────────────────────────
五、如何“一键切换”读写分离

  1. 本地/测试环境想省事——只留 db:、删掉或注释 db1:
    Empty() 返回 true
    • InitDB 不注册 dbresolver → 整个程序只使用单库

  2. 生产环境要上读写分离——补全 db1:
    • 下次启动时 Empty() 变 false
    • dbresolver 生效,写库指向 db1,读库指向 db

代码无需改动,只改 YAML 即可完成模式切换。

──────────────────────────────────────
六、重点回顾(记住 4 个名词即可)

  1. DB / DB1:YAML 中两套连接参数,分别映射到 conf.DB 结构体
  2. Empty():用来检测写库是否配置
  3. dbresolver.Register:把 Sources(写库)和 Replicas(读库)注册进 GORM
  4. RandomPolicy:多读库时的负载均衡策略

如此,“读写分离”在项目中就靠这短短十几行实现,既灵活(配置即开关),又安全(配置缺失时自动回退单库)。

相关文章:

  • 力反馈手套:工业虚拟现实培训领域的革新者
  • Grounding Language Model with Chunking‑Free In‑Context Retrieval (CFIC)
  • Java八股文——Spring「SpringMVC 篇」
  • Kotlin基础语法四
  • 新能源知识库(37)三代储能电芯介绍
  • 达梦数据库dsc集群+实时主备
  • 前端面试题之将自定义数据结构转化成DOM元素
  • 8.Vue的watch监视
  • Swift基础语法详解
  • C++(5)
  • FreeRTOS互斥量
  • Docker部署minio
  • 归并排序详解:优雅的分治艺术
  • 31.Python编程实战:自动化批量压缩与解压文件
  • 地址解析协议(ARP):交换机中的深度解析与实战指南
  • 扁平风格职场商务通用PPT模版分享
  • 企业级RAG系统架构设计与实现指南(基于Java技术栈)
  • 【技巧】win10和ubuntu互相挂在共享文件夹
  • Python爬虫实战:研究cssselect相关技术
  • Master PDF Editor:全能PDF编辑工具
  • 聊城手机网站建设电话/微信朋友圈推广文案
  • 自适应网站建设方案/广州做网站的公司哪家好
  • 有什么网站是帮别人做设计的/网站百度收录要多久
  • 南京网站优化公司排名/百度top排行榜
  • 常平网站建设/新榜数据平台
  • 前沿科技帮客户做的网站有钱赚吗/免费b站推广