第三章:数据库连接与配置
3.1 支持的数据库
GORM 官方支持以下数据库:
| 数据库 |
驱动包 |
DSN 示例 |
| MySQL |
gorm.io/driver/mysql |
user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local |
| PostgreSQL |
gorm.io/driver/postgres |
host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai |
| SQLite |
gorm.io/driver/sqlite |
test.db 或 :memory: |
| SQL Server |
gorm.io/driver/sqlserver |
sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm |
| ClickHouse |
gorm.io/driver/clickhouse |
tcp://localhost:9000?database=gorm&username=gorm&password=gorm&read_timeout=10&write_timeout=20 |
3.2 MySQL 连接详解
基础连接
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("数据库连接失败: " + err.Error())
}
}
DSN 参数说明
| 参数 |
说明 |
建议值 |
charset |
字符集 |
utf8mb4(支持 emoji) |
parseTime |
解析时间类型 |
True |
loc |
时区 |
Local 或 Asia/Shanghai |
timeout |
连接超时 |
10s |
readTimeout |
读取超时 |
30s |
writeTimeout |
写入超时 |
30s |
interpolateParams |
参数插值 |
true(减少预处理) |
multiStatements |
多语句执行 |
按需开启 |
高级 DSN 配置
dsn := "user:password@tcp(127.0.0.1:3306)/dbname?" +
"charset=utf8mb4&" +
"parseTime=True&" +
"loc=Asia%2FShanghai&" +
"timeout=10s&" +
"readTimeout=30s&" +
"writeTimeout=30s&" +
"maxAllowedPacket=0&" + // 0 表示使用驱动默认最大值
"allowNativePasswords=true"
使用现有连接
import (
"database/sql"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
sqlDB, err := sql.Open("mysql", dsn)
// 可以在这里设置 sqlDB 的连接池参数
db, err := gorm.Open(mysql.New(mysql.Config{
Conn: sqlDB,
}), &gorm.Config{})
3.3 PostgreSQL 连接详解
基础连接
import "gorm.io/driver/postgres"
dsn := "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
DSN 参数说明
| 参数 |
说明 |
host |
主机地址 |
port |
端口(默认 5432) |
user |
用户名 |
password |
密码 |
dbname |
数据库名 |
sslmode |
SSL 模式:disable, require, verify-ca, verify-full |
TimeZone |
时区 |
search_path |
schema 搜索路径 |
application_name |
应用名称(用于 pg_stat_activity) |
使用高级配置
dsn := "postgres://user:password@localhost:5432/dbname?" +
"sslmode=disable&" +
"connect_timeout=10&" +
"application_name=myapp&" +
"search_path=public,myschema"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
3.4 SQLite 连接详解
文件模式
import "gorm.io/driver/sqlite"
// 文件数据库
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
内存模式
// 纯内存数据库(连接关闭后数据丢失)
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
// 命名内存数据库(多个连接可共享)
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
高级选项
db, err := gorm.Open(sqlite.Open("gorm.db?_fk=1"), &gorm.Config{})
// _fk=1 启用外键约束(SQLite 默认关闭)
3.5 连接池配置
import (
"time"
"gorm.io/gorm"
)
func setupConnectionPool(db *gorm.DB) {
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
// 设置空闲连接池中的最大连接数
sqlDB.SetMaxIdleConns(10)
// 设置打开数据库连接的最大数量
sqlDB.SetMaxOpenConns(100)
// 设置连接可复用的最大时间
sqlDB.SetConnMaxLifetime(time.Hour)
// 设置连接在池中保持空闲的最大时间(Go 1.15+)
sqlDB.SetConnMaxIdleTime(30 * time.Minute)
}
连接池参数建议
| 场景 |
MaxIdleConns |
MaxOpenConns |
ConnMaxLifetime |
| 小型应用 |
5-10 |
20-50 |
1h |
| 中型应用 |
10-25 |
50-100 |
1h |
| 大型应用 |
25-50 |
100-200 |
30m |
| 高并发 |
50+ |
200+ |
30m |
3.6 日志配置
内置日志级别
import (
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
"os"
"time"
)
func main() {
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略 ErrRecordNotFound
Colorful: true, // 彩色打印
},
)
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
Logger: newLogger,
})
}
日志级别
| 级别 |
说明 |
logger.Silent |
不打印任何日志 |
logger.Error |
仅打印错误日志 |
logger.Warn |
打印警告和错误 |
logger.Info |
打印所有 SQL(开发推荐) |
自定义日志
import (
"context"
"time"
"gorm.io/gorm/logger"
)
type CustomLogger struct {
logger.Interface
}
func (l *CustomLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
// 自定义追踪逻辑,如发送到日志收集系统
sql, rows := fc()
duration := time.Since(begin)
// 记录到文件或监控
if duration > 100*time.Millisecond {
// 慢查询告警
}
}
3.7 GORM 配置选项
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
// 跳过默认事务
SkipDefaultTransaction: false,
// 命名策略
NamingStrategy: schema.NamingStrategy{
TablePrefix: "t_", // 表名前缀
SingularTable: true, // 使用单数表名
NameReplacer: strings.NewReplacer("CID", "Cid"), // 名称替换
},
// 全表更新保护
AllowGlobalUpdate: false, // 默认 false,防止无 WHERE 的 UPDATE/DELETE
// 禁用自动 Ping
DisableAutomaticPing: false,
// 禁用嵌套事务
DisableNestedTransaction: false,
// 允许返回非受影响记录的错误
AllowErrorNonConforming: false,
// 禁用外键约束(指定 OnDelete/OnUpdate 时)
DisableForeignKeyConstraintWhenMigrating: false,
// 忽略迁移时的关联引用
IgnoreRelationshipsWhenMigrating: false,
// 禁用自动递增偏好(某些数据库需要)
DisableAutoIncrementPreference: false,
// 自定义日志
Logger: logger.Default,
})
3.8 多数据库配置
读写分离
type DBPool struct {
Write *gorm.DB
Read *gorm.DB
}
func NewDBPool() *DBPool {
// 主库(写)
writeDB, err := gorm.Open(mysql.Open(writeDSN), &gorm.Config{})
// 从库(读)
readDB, err := gorm.Open(mysql.Open(readDSN), &gorm.Config{})
return &DBPool{
Write: writeDB,
Read: readDB,
}
}
// 使用
pool.Write.Create(&user) // 写操作
pool.Read.First(&user, 1) // 读操作
动态切换数据库
db.Use(dbresolver.Register(dbresolver.Config{
// 主库
Sources: []gorm.Dialector{mysql.Open("main_dsn")},
// 从库
Replicas: []gorm.Dialector{
mysql.Open("replica1_dsn"),
mysql.Open("replica2_dsn"),
},
// 负载均衡策略
Policy: dbresolver.RandomPolicy{},
}).
Register(dbresolver.Config{
Sources: []gorm.Dialector{mysql.Open("user_main_dsn")},
Replicas: []gorm.Dialector{mysql.Open("user_replica_dsn")},
}, &User{}, &Order{}). // 特定模型使用特定配置
SetConnMaxIdleTime(time.Hour).
SetConnMaxLifetime(24 * time.Hour).
SetMaxIdleConns(100).
SetMaxOpenConns(200))
3.9 连接健康检查
import (
"context"
"time"
)
func CheckDBHealth(db *gorm.DB) error {
sqlDB, err := db.DB()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
return sqlDB.PingContext(ctx)
}
// 统计信息
func GetDBStats(db *gorm.DB) sql.DBStats {
sqlDB, _ := db.DB()
return sqlDB.Stats()
}
3.10 完整配置示例
package database
import (
"fmt"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/plugin/dbresolver"
)
var DB *gorm.DB
func InitDB() error {
var err error
// DSN 配置
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
"root",
"password",
"127.0.0.1",
3306,
"myapp",
)
// GORM 配置
config := &gorm.Config{
Logger: logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags),
logger.Config{
SlowThreshold: time.Second,
LogLevel: logger.Info,
Colorful: true,
},
),
NamingStrategy: schema.NamingStrategy{
TablePrefix: "app_",
SingularTable: true,
},
DisableForeignKeyConstraintWhenMigrating: true,
}
// 连接数据库
DB, err = gorm.Open(mysql.Open(dsn), config)
if err != nil {
return fmt.Errorf("数据库连接失败: %w", err)
}
// 配置连接池
sqlDB, err := DB.DB()
if err != nil {
return err
}
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(100)
sqlDB.SetConnMaxLifetime(time.Hour)
sqlDB.SetConnMaxIdleTime(30 * time.Minute)
// 检查连接
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err = sqlDB.PingContext(ctx); err != nil {
return fmt.Errorf("数据库 ping 失败: %w", err)
}
return nil
}
func CloseDB() error {
if DB != nil {
sqlDB, err := DB.DB()
if err != nil {
return err
}
return sqlDB.Close()
}
return nil
}
3.11 练习题
- 配置 MySQL 连接,开启慢查询日志(阈值 500ms)
- 编写连接池配置,适应高并发场景(1000 QPS)
- 实现一个数据库健康检查接口,返回连接池统计信息
3.12 小结
本章详细讲解了 GORM 的数据库连接配置,包括各类数据库的 DSN 格式、连接池优化、日志配置等。合理的连接配置是保证应用性能和稳定性的关键。
本文代码地址:https://github.com/LittleMoreInteresting/gorm_study
欢迎关注公众号,一起学习进步!