数据库索引
- 分析现有索引:使用
EXPLAIN
关键字分析查询语句,查看是否正确使用了现有索引。在Go语言中,可通过 db.Raw("EXPLAIN your_query").Scan(&result)
执行该操作,其中 your_query
是实际的查询语句,result
是存储结果的结构体。
- 添加必要索引:根据查询条件和
EXPLAIN
的结果,添加合适的索引。例如,如果查询经常按 user_id
过滤,可在 users
表的 user_id
字段添加索引。在Go代码中使用GORM可通过 db.AutoMigrate(&User{})
并在结构体字段上标记 gorm:"index"
来实现,如:
type User struct {
ID uint
UserID uint `gorm:"index"`
Name string
}
查询语句优化
- 避免全表扫描:确保查询条件能够利用索引,减少不必要的
SELECT *
,只选择需要的字段。例如,原查询 db.Table("users").Select("*").Where("age >?", 18).Find(&users)
优化为 db.Table("users").Select("name, age").Where("age >?", 18).Find(&users)
。
- 简化子查询:如果存在子查询,尝试将其改写为连接查询。例如,子查询
SELECT * FROM users WHERE user_id IN (SELECT user_id FROM orders WHERE amount > 100)
可改写为连接查询 db.Table("users").Joins("JOIN orders ON users.user_id = orders.user_id").Where("orders.amount > 100").Find(&users)
。
缓存使用
- 选择缓存类型:可选用Redis等缓存数据库。在Go中,使用
go-redis
库操作Redis。
- 缓存查询结果:在查询数据库前先检查缓存中是否有结果。示例代码如下:
package main
import (
"github.com/go-redis/redis/v8"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"context"
)
var rdb *redis.Client
var db *gorm.DB
func init() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
var err error
db, err = gorm.Open(mysql.Open("user:password@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
}
func GetUsers() ([]User, error) {
var users []User
ctx := context.Background()
key := "users_query_result"
result, err := rdb.Get(ctx, key).Bytes()
if err == nil {
// 反序列化结果到users
// 此处假设存在反序列化函数UnMarshalResult
err = UnMarshalResult(result, &users)
if err == nil {
return users, nil
}
}
// 缓存未命中,查询数据库
err = db.Find(&users).Error
if err == nil {
// 将查询结果存入缓存
// 此处假设存在序列化函数MarshalResult
data, err := MarshalResult(users)
if err == nil {
rdb.Set(ctx, key, data, 0)
}
}
return users, err
}
数据库连接池优化
- 合理配置连接池:在Go语言中使用GORM时,可通过
db.DB()
获取底层数据库连接对象,然后设置连接池参数。例如:
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
- 复用连接:确保在代码中正确复用数据库连接,避免频繁创建和关闭连接带来的性能开销。例如,在一个HTTP服务中,可将数据库连接对象作为全局变量,在不同的请求处理函数中复用。