前言
gorm(stars: 29K)是基于go开发的一个ORM工具, sqlx (stars: 12.3K)是一个基于go语言开发的, 在原生go-sql-driver/mysql(stars: 12.4K)上拓展的库.
他们是目前业界用的较多的3个组件, 故此对这几个组件进行一个简单的性能测试.
环境说明
代码语言:txt复制goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
版本: mysql Ver 14.14 Distrib 5.7.20, for macos10.12 (x86_64)
数据量: 2W, 目前仅测试select查询性能, 因为这块我们使用最频繁
表定义
代码语言:sql复制CREATE TABLE `person` (
`first_name` text,
`last_name` text,
`email` text,
`id` int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4;
性能测试
代码语言:go复制package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"testing"
"time"
)
type Person struct {
Id int `db:"id"`
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
Email string `db:"email"`
}
func (p Person) TableName() string {
return "person"
}
func Benchmark(b *testing.B) {
dsn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8&parseTime=True&loc=UTC",
"root", "362427gg", "localhost", 3306, "test")
limits := []int{
5,
50,
500,
10000,
}
// 原生mysql连接池
sqlDB, _ := sql.Open("mysql", dsn)
sqlDB.SetMaxOpenConns(500)
sqlDB.SetMaxIdleConns(100)
// sqlx连接池
sqlxDB, _ := sqlx.Connect("mysql", dsn)
sqlxDB.SetMaxOpenConns(500)
sqlxDB.SetMaxIdleConns(100)
// gorm
gormDB, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{NowFunc: func() time.Time {
return time.Now().UTC().Round(
time.Microsecond)
}})
db, _ := gormDB.DB()
db.SetMaxOpenConns(500)
db.SetMaxIdleConns(100)
for _, lim := range limits {
lim := lim
// Benchmark origin
b.Run(fmt.Sprintf("sql limit:%d", lim), func(b *testing.B) {
for i := 0; i < b.N; i {
q := fmt.Sprintf("SELECT id,first_name,last_name,email FROM test.person ORDER BY id LIMIT %d", lim)
var res []Person
rows, err := sqlDB.Query(q)
if err != nil {
b.Fatal(err)
}
defer rows.Close()
for rows.Next() {
p := Person{}
rows.Scan(&p.Id, &p.FirstName, &p.LastName, &p.Email)
res = append(res, p)
}
}
})
// Benchmark sqlx
b.Run(fmt.Sprintf("sqlx limit:%d", lim), func(b *testing.B) {
for i := 0; i < b.N; i {
q := fmt.Sprintf("SELECT id,first_name,last_name,email FROM test.person ORDER BY id LIMIT %d", lim)
var res []Person
err := sqlxDB.Select(&res, q)
if err != nil {
b.Fatal(err)
}
}
})
// Benchmark gormDB
b.Run(fmt.Sprintf("gormDB limit:%d", lim), func(b *testing.B) {
for i := 0; i < b.N; i {
var res []Person
err := gormDB.Order("id").Limit(lim).Find(&res).Error
if err != nil {
b.Fatal(err)
}
}
})
fmt.Println("==================================================================================================================")
}
}
测试结果
sql原生组件性能最高但是和sqlx相差10~20%, gorm的性能最差, 比原生差10~50%
代码语言:txt复制$ GORM_DIALECT=mysql go test -bench=. -benchmem
goos: darwin
goarch: amd64
pkg: grpc-demo
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Benchmark/sql_limit:5-12 9044 122952 ns/op 2602 B/op 63 allocs/op
Benchmark/sqlx_limit:5-12 9093 124345 ns/op 2889 B/op 70 allocs/op
Benchmark/gormDB_limit:5-12 8854 132475 ns/op 5646 B/op 106 allocs/op
==================================================================================================================
Benchmark/sql_limit:50-12 5750 202876 ns/op 17971 B/op 467 allocs/op
Benchmark/sqlx_limit:50-12 5120 219263 ns/op 19340 B/op 519 allocs/op
Benchmark/gormDB_limit:50-12 4244 262799 ns/op 21851 B/op 689 allocs/op
==================================================================================================================
Benchmark/sql_limit:500-12 1130 1037233 ns/op 161823 B/op 4521 allocs/op
Benchmark/sqlx_limit:500-12 964 1207199 ns/op 167845 B/op 5023 allocs/op
Benchmark/gormDB_limit:500-12 741 1569280 ns/op 187316 B/op 6543 allocs/op
==================================================================================================================
Benchmark/sql_limit:10000-12 100 11728373 ns/op 4648238 B/op 90034 allocs/op
Benchmark/sqlx_limit:10000-12 100 12551046 ns/op 4882252 B/op 100037 allocs/op
Benchmark/gormDB_limit:10000-12 60 19835525 ns/op 5031278 B/op 130076 allocs/op
==================================================================================================================
PASS
ok grpc-demo 14.946s
分析原因
sqlx为啥比gorm快?
(1) interface{}问题
GORM中许多函数入参的数据类型都是interface{},底层又用reflect支持了多种类型,这种实现会导致两个问题:
- reflect导致的底层的性能不高(这点还能接受)
- interface{}如果传入了不支持的复杂数据类型时,排查问题麻烦,往往要运行程序时才会报错
而在sqlx中测试使用的是原生的sql, 基本没有额外的反射操作.
(2) 高频拼接重复SQL
在一个程序运行过程中,执行的SQL语句都比较固定,而变化的往往是参数, 从GORM的实现来看,每次执行都需要重新拼接一次SQL语句,性能肯定也是有影响的.
而在sqlx中测试使用的是原生的sql, 只是做了个简单的字符串format操作