gorm性能优化

GORM 已经优化了许多东西来提高性能,其默认性能对大多数应用来说都够用了。但这里还是有一些关于如何为您的应用改进性能的方法。

禁用默认事务

对于写操作(创建、更新、删除),为了确保数据的完整性,GORM 会将它们封装在事务内运行。但这会降低性能,如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。

 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
 SkipDefaultTransaction: true,
})

缓存预编译语句

执行任何 SQL 时都创建并缓存预编译语句,可以提高后续的调用速度

// 全局模式
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  PrepareStmt: true,
})

// 会话模式
tx := db.Session(&Session{PrepareStmt: true})
tx.First(&user, 1)
tx.Find(&users)
tx.Model(&user).Update("Age", 18)

选择字段

默认情况下,GORM 在查询时会选择所有的字段,您可以使用 Select 来指定您想要的字段

db.Select("Name", "Age").Find(&Users{})
或者这样指定需要的字段

type User struct {
  ID     uint
  Name   string
  Age    int
  Gender string
  // 假设后面还有几百个字段...
}

type APIUser struct {
  ID   uint
  Name string
}

// 查询时会自动选择 `id`、`name` 字段
db.Model(&User{}).Limit(10).Find(&APIUser{})
// SELECT `id`, `name` FROM `users` LIMIT 10

迭代、FindInBatches

用迭代或 in batches 查询并处理记录
Iteration

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Rows()
defer rows.Close()

for rows.Next() {
  var user User
  // ScanRows is a method of `gorm.DB`, it can be used to scan a row into a struct
  db.ScanRows(rows, &user)

  // do something
}

FindInBatches

// batch size 100
result := db.Where("processed = ?", false).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error {
  for _, result := range results {
    // batch processing found records
  }

  tx.Save(&results)

  tx.RowsAffected // number of records in this batch

  batch // Batch 1, 2, 3

  // returns error will stop future batches
  return nil
})

result.Error // returned error
result.RowsAffected // processed records count in all batches

interpolateParams=true

开启 interpolateparams 以减少 roundtrip
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN&interpolateParams=true]

If interpolateParams is true, placeholders (?) in calls to db.Query() and db.Exec() are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with interpolateParams=false.

package main
 
import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)
 
func main() {
    //主库用读写账户 建立mysql链接时使用参数interpolateParams=true
    db_master, err := sql.Open("mysql", "master:@tcp(127.0.0.1:3306)/db_test?charset=utf8&interpolateParams=true")
    if err != nil { 
        panic(err.Error()) //只是举例,真实使用中需要对错误进行处理和返回
    }
    defer db_master.Close()
    //从库用只读账户 建立mysql链接时使用参数interpolateParams=true
    db_slave, err := sql.Open("mysql", "slave:@tcp(127.0.0.1:3306)/db_test?charset=utf8&interpolateParams=true")
    if err != nil { 
        panic(err.Error())
    }
    defer db_slave.Close()
 
    rows, err := db_slave.Query("SELECT * FROM tbl_user WHERE user_id = ?", 1)
    if err != nil { 
        panic(err.Error()) 
    }
}
 

但注意interpolateParams=true不可以与以下多字节编码共同使用(multibyte encodings BIG5, CP932, GB2312, GBK or SJIS),因为他们会引起sql注入脆弱性。

读写分离

通过读写分离提高数据吞吐量,查看 Database Resolver 获取详情

关闭ping

在完成初始化后,GORM 会自动 ping 数据库以检查数据库的可用性,若要禁用该特性,可将其设置为 true.如果有其他措施保证数据库是可用的,该项也可用关闭

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
  DisableAutomaticPing: true,
})

参考

1、https://gorm.io/zh_CN/docs/performance.html
2、https://github.com/go-sql-driver/mysql#interpolateparams
3、https://blog.csdn.net/weixin_29724477/article/details/112098670

你可能感兴趣的:(gorm性能优化)