You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入gorm.Model后created_at与updated_at未自动建索引,如何批量添加?

给GORM所有模型的created_at和updated_at自动添加索引的方案

问题背景

我在Go应用中使用GORM作为ORM,所有模型结构体都嵌入了gorm.Model。日期范围过滤是最常见的数据库查询场景之一,我原本以为created_at和updated_at列会自动创建索引,但实际并非如此。

慢查询分析显示日期范围查询执行了顺序扫描:

+----------------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN                                                                                                                       |
|----------------------------------------------------------------------------------------------------------------------------------|
| Aggregate  (cost=969.33..969.34 rows=1 width=8) (actual time=2.675..2.676 rows=1 loops=1)                                        |
|   ->  Sort  (cost=969.31..969.31 rows=1 width=214) (actual time=2.672..2.672 rows=0 loops=1)                                     |
|         Sort Key: analyses.updated_at DESC                                                                                       |
|         Sort Method: quicksort  Memory: 25kB                                                                                     |
|         ->  Seq Scan on analyses  (cost=0.00..969.30 rows=1 width=214) (actual time=2.666..2.666 rows=0 loops=1)                 |
|               Filter: ((status = 3) AND (updated_at < (now())::date) AND (updated_at >= ((now() + '00:00:10'::interval))::date)) |
|               Rows Removed by Filter: 9012                                                                                       |
| Planning Time: 0.217 ms                                                                                                          |
| Execution Time: 2.713 ms 

表结构证实created_at和updated_at没有索引:

Indexes:
    "analyses_pkey" PRIMARY KEY, btree (id)
    "idx_ecosystem_name_version" UNIQUE, btree (ecosystem, name, version)
    "idx_analyses_external_id" UNIQUE, btree (external_id)
    "idx_analyses_deleted_at" btree (deleted_at)
    "idx_analyses_source" btree (source)

请问给所有模型的created_at和updated_at自动添加索引的推荐方式是什么?


推荐方案

1. 自定义基础模型替代gorm.Model

直接定义自己的基础模型,给CreatedAt和UpdatedAt字段添加索引标签,所有业务模型嵌入这个自定义模型:

import (
  "gorm.io/gorm"
  "time"
)

// 自定义基础模型,替代gorm.Model
type BaseModel struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time      `gorm:"index"` // 为created_at添加索引
  UpdatedAt time.Time      `gorm:"index"` // 为updated_at添加索引
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

// 业务模型示例:嵌入自定义基础模型
type Analysis struct {
  BaseModel
  Ecosystem string
  Name      string
  Version   string
  // 其他业务字段...
}

执行AutoMigrate时,GORM会自动为所有嵌入BaseModel的表创建created_at和updated_at的索引。

2. 批量添加索引(适用于已使用原生gorm.Model的场景)

如果已有大量模型依赖原生gorm.Model,可以通过以下方式批量补加索引:

方法A:启动时批量执行SQL

遍历所有目标表,执行创建索引的SQL语句:

import (
  "fmt"
  "gorm.io/gorm"
)

func AddDateIndexes(db *gorm.DB) error {
  // 替换为实际业务中的表名列表
  tables := []string{"analyses", "users", "orders"}

  for _, table := range tables {
    // 创建created_at索引
    if err := db.Exec(fmt.Sprintf("CREATE INDEX IF NOT EXISTS idx_%s_created_at ON %s(created_at)", table, table)).Error; err != nil {
      return err
    }
    // 创建updated_at索引
    if err := db.Exec(fmt.Sprintf("CREATE INDEX IF NOT EXISTS idx_%s_updated_at ON %s(updated_at)", table, table)).Error; err != nil {
      return err
    }
  }
  return nil
}

在应用启动初始化数据库后调用该函数即可。

方法B:使用GORM迁移功能

创建迁移文件管理索引添加操作,适合需要版本控制的场景:

import "gorm.io/gorm"

func AddDateIndexesMigration(db *gorm.DB) error {
  return db.Transaction(func(tx *gorm.DB) error {
    // 为analyses表添加索引
    if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_analyses_created_at ON analyses(created_at)").Error; err != nil {
      return err
    }
    if err := tx.Exec("CREATE INDEX IF NOT EXISTS idx_analyses_updated_at ON analyses(updated_at)").Error; err != nil {
      return err
    }
    // 其他表同理添加索引...
    return nil
  })
}

3. 额外优化建议

  • 对于数据量大的表,创建索引会短暂锁表,建议在业务低峰期执行。
  • 针对你慢查询中status + updated_at的组合过滤场景,可创建复合索引替代单独索引,性能更优:
    CREATE INDEX IF NOT EXISTS idx_analyses_status_updated_at ON analyses(status, updated_at);
    
  • 避免过度索引:如果部分表很少用到日期范围查询,可不为其添加索引,减少写入操作的性能开销。

内容的提问来源于stack exchange,提问作者abhisek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:27:37