索引管理

索引是数据库性能优化的核心。合理的索引能让查询快几个数量级,但索引过多也会影响写入性能。

创建索引

模型中定义索引:

type User struct {
    ID    uint
    Name  string `gorm:"index"`
    Email string `gorm:"uniqueIndex"`
}

AutoMigrate 时自动创建:

db.AutoMigrate(&User{})

索引命名

默认索引名是 idx_表名_字段名。自定义索引名:

type User struct {
    Name string `gorm:"index:idx_name"`
}

复合索引

多字段组合索引:

type User struct {
    Name string `gorm:"index:idx_name_city"`
    City string `gorm:"index:idx_name_city"`
}

指定顺序:

type User struct {
    Name string `gorm:"index:idx_name_city,priority:1"`
    City string `gorm:"index:idx_name_city,priority:2"`
}

优先级数字越小越靠前。

唯一索引

type User struct {
    Email string `gorm:"uniqueIndex"`
}

复合唯一索引:

type User struct {
    Name  string `gorm:"uniqueIndex:idx_name_department"`
    Dept  string `gorm:"uniqueIndex:idx_name_department"`
}

条件索引

部分数据库支持条件索引:

type User struct {
    Status string `gorm:"index:idx_active,option:WHERE status = 'active'"`
}

PostgreSQL 支持,MySQL 不支持。

全文索引

type Article struct {
    Title   string `gorm:"type:varchar(255);index:idx_fulltext,type:FULLTEXT"`
    Content string `gorm:"type:text;index:idx_fulltext,type:FULLTEXT"`
}

MySQL 支持。

手动创建索引

db.Migrator().CreateIndex(&User{}, "Name")
db.Migrator().CreateIndex(&User{}, "idx_name")

删除索引

db.Migrator().DropIndex(&User{}, "Name")
db.Migrator().DropIndex(&User{}, "idx_name")

检查索引存在

if db.Migrator().HasIndex(&User{}, "idx_name") {
    db.Migrator().DropIndex(&User{}, "idx_name")
}

原生 SQL 创建索引

复杂索引用原生 SQL:

db.Exec(`CREATE INDEX idx_user_name ON users(name)`)

db.Exec(`CREATE UNIQUE INDEX idx_user_email ON users(email)`)

db.Exec(`
    CREATE INDEX idx_user_name_city
    ON users(name, city)
`)

db.Exec(`
    CREATE INDEX idx_user_created
    ON users(created_at DESC)
`)

PostgreSQL 条件索引:

db.Exec(`
    CREATE INDEX idx_active_users
    ON users(email)
    WHERE status = 'active'
`)

索引类型

B-Tree 索引

默认类型,适合等值查询和范围查询:

Name string `gorm:"index"`

Hash 索引

只支持等值查询,某些数据库支持:

db.Exec(`CREATE INDEX idx_user_name_hash ON users USING HASH (name)`)

全文索引

文本搜索:

db.Exec(`CREATE FULLTEXT INDEX idx_article_content ON articles(content)`)

索引策略

何时创建索引

  • WHERE 条件字段
  • JOIN 关联字段
  • ORDER BY 排序字段
  • GROUP BY 分组字段

何时不创建索引

  • 小表(几百条记录)
  • 频繁更新的字段
  • 区分度低的字段(如性别)
  • 很少查询的字段

复合索引原则

最左前缀原则:索引 (a, b, c) 可以用于:

  • WHERE a = ?
  • WHERE a = ? AND b = ?
  • WHERE a = ? AND b = ? AND c = ?

不能用于:

  • WHERE b = ?
  • WHERE c = ?

把最常用的查询条件放在最左边。

索引优化示例

慢查询分析

EXPLAIN SELECT * FROM users WHERE name = '张三';

查看是否使用索引。

强制使用索引

db.Clauses(clause.UseIndex("idx_name")).Find(&users)

忽略索引

db.Clauses(clause.IgnoreIndex("idx_name")).Find(&users)

实际案例

用户表索引

type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string `gorm:"index:idx_name_city;size:50"`
    City      string `gorm:"index:idx_name_city;size:50"`
    Email     string `gorm:"uniqueIndex;size:100"`
    Phone     string `gorm:"uniqueIndex:idx_phone;size:20"`
    Status    string `gorm:"index;size:20"`
    CreatedAt time.Time `gorm:"index"`
}

订单表索引

type Order struct {
    ID          uint `gorm:"primaryKey"`
    UserID      uint `gorm:"index"`
    Status      string `gorm:"index:idx_status_created"`
    CreatedAt   time.Time `gorm:"index:idx_status_created"`
    Amount      float64
}

// 复合索引:按状态和时间查询
// SELECT * FROM orders WHERE status = 'paid' ORDER BY created_at DESC

文章表索引

type Article struct {
    ID        uint `gorm:"primaryKey"`
    Title     string `gorm:"index:idx_fulltext,type:FULLTEXT"`
    Content   string `gorm:"index:idx_fulltext,type:FULLTEXT"`
    AuthorID  uint `gorm:"index"`
    Status    string `gorm:"index:idx_status_published"`
    PublishedAt *time.Time `gorm:"index:idx_status_published"`
}

小结

索引是性能优化的利器,但要用对地方。理解索引原理,遵循最左前缀原则,定期检查慢查询。