全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-21 8 分钟 ✍️ juanwangdev

GORM 分表策略实现

GORM 分表通过 Table() 方法动态切换目标表名,结合业务规则实现数据水平拆分。

分表核心机制

Table 方法动态切换

Go
// 动态指定表名
db.Table("orders_202605").Create(&order)
db.Table("orders_202605").Where("user_id = ?", uid).Find(&orders)

Scopes 复用分表逻辑

Go
func ByMonth(table string) func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        return db.Table(table)
    }
}

// 使用
db.Scopes(ByMonth("orders_202605")).Create(&order)

按时间分表

月份分表实现

Go
type Order struct {
    ID        uint      `gorm:"primaryKey"`
    UserID    uint      `gorm:"index"`
    Amount    decimal.Decimal
    CreatedAt time.Time
}

func getOrderTable(t time.Time) string {
    return fmt.Sprintf("orders_%d%02d", t.Year(), t.Month())
}

func CreateOrder(db *gorm.DB, order *Order) error {
    table := getOrderTable(order.CreatedAt)
    return db.Table(table).Create(order).Error
}

func QueryOrdersByMonth(db *gorm.DB, year, month int) ([]Order, error) {
    table := fmt.Sprintf("orders_%d%02d", year, month)
    var orders []Order
    err := db.Table(table).Where("created_at >= ? AND created_at < ?", 
        time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC),
        time.Date(year, time.Month(month+1), 1, 0, 0, 0, 0, time.UTC),
    ).Find(&orders).Error
    return orders, err
}

按哈希分表

用户 ID 哈希分表

Go
func getUserTable(userID uint) string {
    // 按 16 张表哈希路由
    shard := userID % 16
    return fmt.Sprintf("users_%02d", shard)
}

func CreateUser(db *gorm.DB, user *User) error {
    table := getUserTable(user.ID)
    return db.Table(table).Create(user).Error
}

func GetUserByID(db *gorm.DB, userID uint) (*User, error) {
    table := getUserTable(userID)
    var user User
    err := db.Table(table).First(&user, userID).Error
    return &user, err
}

哈希分表通用函数

Go
func HashSharding(key string, shards int) string {
    h := fnv.New32a()
    h.Write([]byte(key))
    return fmt.Sprintf("table_%02d", h.Sum32()%uint32(shards))
}

自动建表处理

迁移检查

Go
func AutoMigrateShard(db *gorm.DB, table string) error {
    // 检查表是否存在
    if !db.Migrator().HasTable(table) {
        // 克隆表结构并创建
        return db.Exec("CREATE TABLE " + table + " LIKE orders_template").Error
    }
    return nil
}

注意: 分表场景下 AutoMigrate 需针对每个分片表单独执行,建议预先创建未来一段时间的分表。

跨分表查询

合并查询结果

Go
func QueryAcrossShards(db *gorm.DB, months []string) ([]Order, error) {
    var allOrders []Order
    for _, month := range months {
        table := "orders_" + month
        var orders []Order
        if err := db.Table(table).Find(&orders).Error; err != nil {
            return nil, err
        }
        allOrders = append(allOrders, orders...)
    }
    return allOrders, nil
}

注意: 跨分表查询无法使用单表索引优化,数据量大时应限制查询范围或引入 ES 等检索引擎。

要点总结

  • 使用 Table() 方法动态切换目标表名
  • 按时间分表适合日志、订单等时序数据
  • 按哈希分表适合用户、账户等均匀分布数据
  • Scopes 可封装分表逻辑提高复用性
  • 跨分表查询需合并结果,性能较差,应限制查询范围

存放路径:D:\git2\jwdev\articles\GORM\专家\分库分表与多租户\分表策略实现.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 GORM 分库路由配置
下一篇 → GORM 多租户数据隔离
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库