如何在Golang中适配AWS RDS MySQL主从复制实现读写分离?
针对你的AWS RDS一主两从架构和Golang读写分离需求,我给你几个实用的落地方案,你可以根据项目情况选择:
方案1:利用AWS RDS只读端点(最省心)
AWS RDS本身提供了只读端点,这个端点会自动把读请求负载均衡到所有可用的从库上,还能自动处理从库的故障转移——当某个从库挂了,RDS会自动将其从负载池中移除,完全不需要你自己维护逻辑。
代码实现
import ( "database/sql" "time" _ "github.com/go-sql-driver/mysql" ) var ( masterDB *sql.DB // 主库连接,负责写操作 readDB *sql.DB // 只读端点连接,负责读操作 ) func initDB() error { // 主库DSN:替换成你的RDS主库端点 masterDSN := "root:password@tcp(your-master-rds-endpoint:3306)/test" var err error masterDB, err = sql.Open("mysql", masterDSN) if err != nil { return err } // 配置连接池参数,根据业务量调整 masterDB.SetMaxOpenConns(20) masterDB.SetMaxIdleConns(10) masterDB.SetConnMaxLifetime(1 * time.Hour) // RDS只读端点DSN:替换成你的只读端点 readDSN := "root:password@tcp(your-read-replica-rds-endpoint:3306)/test" readDB, err = sql.Open("mysql", readDSN) if err != nil { return err } // 同样配置读连接池参数 readDB.SetMaxOpenConns(20) readDB.SetMaxIdleConns(10) readDB.SetConnMaxLifetime(1 * time.Hour) // 验证连接可用性 if err := masterDB.Ping(); err != nil { return err } if err := readDB.Ping(); err != nil { return err } return nil } // 封装写操作:强制走主库 func executeWrite(query string, args ...interface{}) (sql.Result, error) { return masterDB.Exec(query, args...) } // 封装读操作:走只读端点(自动负载均衡到从库) func executeRead(query string, args ...interface{}) (*sql.Rows, error) { return readDB.Query(query, args...) } // 针对单行查询的封装 func executeReadRow(query string, args ...interface{}) *sql.Row { return readDB.QueryRow(query, args...) }
优缺点
- ✅ 优点:零额外维护成本,RDS自动处理从库负载均衡、故障转移;代码改动最小,只需要新增一个只读连接。
- ❌ 缺点:依赖AWS RDS特性,如果未来迁移到其他云厂商,需要调整方案。
方案2:手动封装读写连接池(轻量可控)
如果不想依赖云厂商特性,你可以自己维护主库和多个从库的连接池,手动实现读请求的负载均衡逻辑(比如随机、轮询)。
代码实现
import ( "database/sql" "math/rand" "time" _ "github.com/go-sql-driver/mysql" ) var ( masterDB *sql.DB slaveDBs []*sql.DB randSeed = rand.New(rand.NewSource(time.Now().UnixNano())) ) func initDB() error { // 初始化主库连接 masterDSN := "root:password@tcp(your-master-rds-endpoint:3306)/test" var err error masterDB, err = sql.Open("mysql", masterDSN) if err != nil { return err } masterDB.SetMaxOpenConns(20) masterDB.SetMaxIdleConns(10) masterDB.SetConnMaxLifetime(1 * time.Hour) // 初始化所有从库连接 slaveDSNs := []string{ "root:password@tcp(your-slave1-rds-endpoint:3306)/test", "root:password@tcp(your-slave2-rds-endpoint:3306)/test", } for _, dsn := range slaveDSNs { slaveDB, err := sql.Open("mysql", dsn) if err != nil { return err } slaveDB.SetMaxOpenConns(20) slaveDB.SetMaxIdleConns(10) slaveDB.SetConnMaxLifetime(1 * time.Hour) slaveDBs = append(slaveDBs, slaveDB) } // 验证所有连接 if err := masterDB.Ping(); err != nil { return err } for _, db := range slaveDBs { if err := db.Ping(); err != nil { return err } } return nil } // 获取读连接:随机选择从库,无从库时 fallback 到主库 func getReadDB() *sql.DB { if len(slaveDBs) == 0 { return masterDB } return slaveDBs[randSeed.Intn(len(slaveDBs))] } // 获取写连接:固定使用主库 func getWriteDB() *sql.DB { return masterDB }
使用示例
// 写操作:插入用户 func insertUser(name string) error { _, err := getWriteDB().Exec("INSERT INTO users(name) VALUES(?)", name) return err } // 读操作:查询用户 func getUser(id int) (string, error) { var name string err := getReadDB().QueryRow("SELECT name FROM users WHERE id=?", id).Scan(&name) return name, err }
优缺点
- ✅ 优点:不依赖任何第三方库或云厂商特性,完全可控;可以自定义负载均衡策略(比如轮询、加权随机)。
- ❌ 缺点:需要自己实现故障检测和切换逻辑(比如某个从库挂了要从列表中移除),维护成本稍高。
方案3:使用ORM框架(如GORM)开箱即用
如果你的项目已经在使用ORM,比如GORM v2,它内置了读写分离的支持,配置起来非常简单,还能自动处理负载均衡和故障切换。
代码实现
import ( "gorm.io/driver/mysql" "gorm.io/gorm" "math/rand" ) func initGORM() (*gorm.DB, error) { // 主库DSN masterDSN := "root:password@tcp(your-master-rds-endpoint:3306)/test?charset=utf8mb4&parseTime=True&loc=Local" // 从库DSNs slaveDSNs := []string{ "root:password@tcp(your-slave1-rds-endpoint:3306)/test?charset=utf8mb4&parseTime=True&loc=Local", "root:password@tcp(your-slave2-rds-endpoint:3306)/test?charset=utf8mb4&parseTime=True&loc=Local", } // 初始化主库连接 masterDB, err := gorm.Open(mysql.Open(masterDSN), &gorm.Config{}) if err != nil { return nil, err } // 初始化从库连接并配置读写分离 var replicas []*gorm.DB for _, dsn := range slaveDSNs { replica, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { return nil, err } replicas = append(replicas, replica) } // 启用读写分离插件,配置随机负载均衡策略 err = masterDB.Use(&gorm.Replica{ Replicas: replicas, Policy: func(_ *gorm.DB) (*gorm.DB, error) { return replicas[rand.Intn(len(replicas))], nil }, }) if err != nil { return nil, err } return masterDB, nil }
使用示例
type User struct { ID uint Name string } // 写操作:自动走主库 func createUser(db *gorm.DB, user *User) error { return db.Create(user).Error } // 读操作:自动走从库 func getUser(db *gorm.DB, id uint) (User, error) { var user User err := db.First(&user, id).Error return user, err }
优缺点
- ✅ 优点:开箱即用,无需自己封装连接池和负载均衡;支持复杂的查询场景,自动区分读写操作。
- ❌ 缺点:如果之前用原生SQL,需要调整代码适配ORM;引入了第三方依赖。
内容的提问来源于stack exchange,提问作者Vish K
相关产品推荐
相关产品推荐

