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

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:多读库时的负载均衡策略

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

http://www.xdnf.cn/news/13482.html

相关文章:

  • iOS开发申请组播/广播权限​
  • 【C/C++】long long 类型传参推荐方式
  • asio之静态互斥量
  • 【PmHub面试篇】集成 Sentinel+OpenFeign实现网关流量控制与服务降级相关面试题解答
  • 远程io模块在汽车流水线的应用
  • 深度学习工具四剑客:Anaconda、Jupyter、PyTorch与CUDA详解
  • 达梦数据库dsc集群+异步主备
  • DeviceNet转Modbus RTU网关在玻璃制造中的关键应用
  • 如何制定兼容多个项目的整体时间计划?
  • Vue.js $emit的介绍和简单使用
  • 【leetcode-合并两个有序链表】
  • Codeforces Round 1029 (Div. 3)
  • C语言数据结构笔记6:使用宏和指针来设置和操作嵌套在结构体中的联合体数组的特定位
  • OC学习—Block初探(简易版)
  • 【实战指南】前端项目Nginx配置全流程:从打包部署到解决跨域/路由循环问题
  • 在C# 中使用建造者模式
  • 算法题(167):FBI树
  • Oracle日志体系和遇到问题后日志排查路径
  • 行为模式-责任链模式
  • 进行性核上性麻痹健康护理指南:全方位照护之道
  • Pytorch 的编程技巧
  • Java八股文——Spring「Spring 篇」
  • 5.4.2树、森林与二叉树的转换
  • 今日行情明日机会——20250611
  • Android GreenDAO 通过 Key 查询数据库数据慢问题优化
  • 13.自治系统路由计算题
  • Node.js:开启现代服务器端编程的新篇章
  • h5fortran 简介与使用指南
  • 新能源知识库(36)什么是BMU
  • 51LA数据分析遇瓶颈?免费统计工具——悟空统计