鉴于db
的类型为*sql.DB
(使用lib/pq
驱动程序,以下代码会导致连接泄漏:
rows, err := db.Query(
"select 1 from things where id = $1",
thing,
)
if err != nil {
return nil, fmt.Errorf("can't select thing (%d): %w", thing, err)
}
found := false
for rows.Next() {
found = true
break
}
反复调用此代码会增加打开的连接数,直到耗尽:
select sum(numbackends) from pg_stat_database;
// 5
// 6
// 7
// ...
// 80
我该如何解决?
您编写的代码有几个问题。您避免连接泄漏的问题的直接答案是关闭rows
迭代器as mentioned in the documentation。因此,您使用自己的答案在正确的轨道上,但是您自己的答案缺少一个重要的细节:rows.Close()
应该仅被调用一次。循环调用将无效。正常的调用方式是在defer
语句中:
rows, err := db.Query(
"select 1 from things where id = $1",
thing,
)
if err != nil {
return nil, fmt.Errorf("can't select thing (%d): %w", thing, err)
}
defer rows.Close()
found := false
for rows.Next() {
found = true
break
}
最后,由于您所关心的只是一个结果,因此完全没有理由获取多行结果集。这种方法实际上隐式地解决了连接泄漏问题,同时还允许Postgres极大地优化查询:
row, err := db.QueryRow(
"select TRUE from things where id = $1 LIMIT 1",
thing,
)
if err != nil {
return nil, fmt.Errorf("can't select thing (%d): %w", thing, err)
}
var found bool
if err := row.Scan(&found); err != nil {
return nil, fmt.Errorf("Failed to scan result: %w", err)
}