使用gorm做服务端,遇到的问题。运行的一些api产生的数据库连接,一直不释放,导致服务运行久了,连接数一直增加,最后服务挂掉。
这个是初始化数据库连接
var SqlDB *sql.DB
func InitOracleDB() {
databaseURL := fmt.Sprintf(`user="%s" password="%s" connectString="%s:%s/%s"`,
global.AppConfig.Server.Oracle.Username,
global.AppConfig.Server.Oracle.Password,
global.AppConfig.Server.Oracle.Ip,
global.AppConfig.Server.Oracle.Port,
global.AppConfig.Server.Oracle.Service,
)
sqlDB, err := sql.Open("godror", databaseURL)
if err != nil {
log.Fatalln("数据库连接失败...,err:", err)
}
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(30)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
//sqlDB.SetConnMaxLifetime(time.Hour)
SqlDB = sqlDB
// 设置日志
db, err := gorm.Open(oracle.New(oracle.Config{Conn: sqlDB}), &gorm.Config{
Logger: gormLog(),
NamingStrategy: schema.NamingStrategy{
TablePrefix: "", // table name prefix, table for `User` would be `t_users`
SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled
},
})
if err != nil {
log.Fatalln("数据库连接失败...,err:", err)
}
if global.AppConfig.Server.Mode == "release" {
global.DB = db
} else {
global.DB = db.Debug()
}
}
补充今天对api进行逐一分析:发现问题在于gorm中的会话模式,会产生新的数据库连接,创建一个Session后每次执行sql的时候都会导致tcp连接数量的增加,而且等待很久也不释放,问下大家有没有好的解决方法?
执行删除不增加:
str := "delete from objs_attent where Id=123"
err := global.DB.Exec(str).Error
执行查询不增加:
str := `with a1 as (SELECT ObjId as id,ObjName as value from phar_define where UseState > 0 ) select id as "id",value as "value",'phar' as "type" from a1 where rownum<=10`
结构体直接查询不增加:
var selectList []po.DicSignKind
global.DB.Model(&po.DicSignKind{}).Find(&selectList)
执行关联,统计数量不增加:
var totalCount int64
sql2 := global.DB.
Model(&po.PharDefine{}).
Select("phar_define.ObjId,ObjCode,ObjName,dic_sign_kind.Name as Type,Sign,phar_define.RtuCode,phar_state.StateId as StateId,b.dept_name as OrganName,c.dept_name as ChannelName,phar_define.UseState,phar_define.Organ,Channel").
Joins("left join phar_state on phar_state.ObjId=phar_define.ObjId"). // 连接航标状态获取终端号
Joins("left join dic_sign_kind on phar_define.SignKind=dic_sign_kind.Code"). // 连接航标类型
Joins("left join sys_dept b on phar_define.Organ=b.dept_id").
Joins("left join sys_dept c on phar_define.Channel=c.dept_id")
sql2.Count(&totalCount).
Find(&result)
创建session并执行查询则会增加连接
sql= global.DB.Table("phar_state").Where("RtuCode != 0")
sql = sql.Session(&gorm.Session{PrepareStmt: true}) // 保存前面拼接的SQL条件
err =sql.Where("RtuCode != 0").Find(&result).Error
比如如何锁定新的连接并删除该连接,我尝试用
kdb,err:=sql.Statement.DB.DB() 或者kdb,err :=global.DB.DB()
然后kdb.Close()
但是测试发现都是删除的初始数据库连接,而不是Session执行产生的新连接
根据你提供的代码,你的 SqlDB 是一个全局变量,可能在其他地方也被使用,而 global.DB 是一个 GORM 对象,也可能在其他地方被使用。如果在其他地方也使用了这些对象,那么这些对象的连接也会一直保持,导致连接数增加,最终导致服务挂掉。
为了解决这个问题,你可以在使用完数据库连接之后,显式地关闭连接。例如,在使用 global.DB 之后,调用 global.DB.Close() 关闭连接。另外,你也可以尝试使用连接池,以便更好地管理数据库连接。对于 GORM,可以使用 DB.DB() 获取其底层的数据库连接对象,然后进行连接池配置,例如:
db, err := gorm.Open(oracle.New(oracle.Config{Conn: sqlDB}), &gorm.Config{
Logger: gormLog(),
NamingStrategy: schema.NamingStrategy{
TablePrefix: "", // table name prefix, table for `User` would be `t_users`
SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled
},
})
if err != nil {
log.Fatalln("数据库连接失败...,err:", err)
}
sqlDB, err := db.DB()
if err != nil {
log.Fatalln("获取底层数据库连接失败...,err:", err)
}
sqlDB.SetMaxIdleConns(10)
sqlDB.SetMaxOpenConns(30)
这里通过 db.DB() 获取 GORM 的底层连接对象,然后进行连接池的配置。
您遇到的问题是因为连接没有被正确释放,导致连接池中的连接数不断增加。根据您提供的代码,可以考虑在每次使用完数据库连接之后,将连接返回给连接池。
您可以在调用数据库操作的函数中,使用defer语句将连接返回给连接池。例如:
func getUsers() ([]User, error) {
var users []User
// 从连接池中获取连接
db := global.DB
// 查询用户
result := db.Find(&users)
if result.Error != nil {
return nil, result.Error
}
// 使用defer语句将连接返回给连接池
defer db.Close()
return users, nil
}
另外,您可以考虑在连接池中设置SetConnMaxLifetime属性,该属性定义连接可复用的最大时间,避免连接长时间占用。
sqlDB.SetConnMaxLifetime(time.Hour)
您也可以将SetConnMaxLifetime取消注释,设置连接可复用的最大时间。当连接超过该时间时,将会被关闭并从连接池中移除。
最后,您可以使用db.DB().Stats()方法查看连接池的状态信息,包括连接数、正在使用的连接数、空闲连接数等。