gin-base/db/core/read_write.go

125 lines
2.4 KiB
Go

package core
import (
"database/sql"
"sync"
"sync/atomic"
)
// ReadWriteDB 读写分离数据库连接
type ReadWriteDB struct {
master *sql.DB // 主库(写)
slaves []*sql.DB // 从库列表(读)
policy ReadPolicy // 读负载均衡策略
counter uint64 // 轮询计数器
mu sync.RWMutex // 读写锁
}
// NewReadWriteDB 创建读写分离数据库连接
func NewReadWriteDB(master *sql.DB, slaves []*sql.DB, policy ReadPolicy) *ReadWriteDB {
return &ReadWriteDB{
master: master,
slaves: slaves,
policy: policy,
}
}
// GetMaster 获取主库连接(用于写操作)
func (rw *ReadWriteDB) GetMaster() *sql.DB {
return rw.master
}
// GetSlave 获取从库连接(用于读操作)
func (rw *ReadWriteDB) GetSlave() *sql.DB {
rw.mu.RLock()
defer rw.mu.RUnlock()
if len(rw.slaves) == 0 {
// 没有从库,使用主库
return rw.master
}
switch rw.policy {
case Random:
// 随机选择一个从库
idx := int(atomic.LoadUint64(&rw.counter)) % len(rw.slaves)
return rw.slaves[idx]
case RoundRobin:
// 轮询选择从库
idx := int(atomic.AddUint64(&rw.counter, 1)) % len(rw.slaves)
return rw.slaves[idx]
case LeastConn:
// 选择连接数最少的从库(简化实现)
return rw.selectLeastConn()
default:
return rw.slaves[0]
}
}
// selectLeastConn 选择连接数最少的从库
func (rw *ReadWriteDB) selectLeastConn() *sql.DB {
if len(rw.slaves) == 0 {
return rw.master
}
minConn := -1
selected := rw.slaves[0]
for _, slave := range rw.slaves {
stats := slave.Stats()
openConnections := stats.OpenConnections
if minConn == -1 || openConnections < minConn {
minConn = openConnections
selected = slave
}
}
return selected
}
// AddSlave 添加从库
func (rw *ReadWriteDB) AddSlave(slave *sql.DB) {
rw.mu.Lock()
defer rw.mu.Unlock()
rw.slaves = append(rw.slaves, slave)
}
// RemoveSlave 移除从库
func (rw *ReadWriteDB) RemoveSlave(slave *sql.DB) {
rw.mu.Lock()
defer rw.mu.Unlock()
for i, s := range rw.slaves {
if s == slave {
rw.slaves = append(rw.slaves[:i], rw.slaves[i+1:]...)
break
}
}
}
// Close 关闭所有连接
func (rw *ReadWriteDB) Close() error {
rw.mu.Lock()
defer rw.mu.Unlock()
// 关闭主库
if rw.master != nil {
if err := rw.master.Close(); err != nil {
return err
}
}
// 关闭所有从库
for _, slave := range rw.slaves {
if err := slave.Close(); err != nil {
return err
}
}
return nil
}