
使用 `defer rows.close()` 可以确保数据库结果集在函数退出时自动关闭,避免因遗漏调用 `close()` 导致连接泄漏和资源耗尽。
在 Go 的数据库操作中,*sql.Rows 对象代表查询结果集,它底层持有一个数据库连接(或连接池中的一个活跃会话)。虽然 rows.Close() 并不直接关闭底层数据库连接(连接通常由连接池复用),但它会释放该结果集占用的连接资源,使其可被后续查询重用。若忘记调用 rows.Close(),尤其在高频查询或长生命周期函数中,会导致连接池“假性耗尽”——即连接被结果集长期占用而无法归还,最终引发 sql: connection pool exhausted 等错误。
最简洁、惯用且安全的做法是结合 defer 语句:
rows, err := db.Query("SELECT id, name FROM users WHERE active = $1", true)
if err != nil {
log.Fatal(err)
}
defer rows.Close() // ✅ 在函数返回前自动执行,无论是否发生 panic 或提前 return
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Printf("scan error: %v", err)
continue // 或 break,视业务逻辑而定
}
// 处理单行数据
fmt.Printf("User %d: %s\n", id, name)
}
// 检查 rows.Next() 循环后是否发生迭代错误(如网络中断、类型不匹配)
if err := rows.Err(); err != nil {
log.Printf("rows iteration error: %v", err)
}⚠️ 关键注意事项:
- defer 必须在 rows 成功创建后立即调用(如示例中紧随 db.Query 之后),否则若 Query 返回错误,rows 为 nil,defer rows.Close() 将 panic;
- defer 的执行时机是包含它的函数即将返回时,因此它适用于函数作用域内的资源清理,不适用于需提前释放资源的场景(此时仍需显式 rows.Close());
- 即使 for rows.Next() 提前 break 或发生 panic,defer rows.Close() 依然保证执行;
- 切勿在循环内部对同一个 rows 多次 defer rows.Close(),会导致重复关闭(虽 sql.Rows.Close() 是幂等的,但属不良实践)。
✅ 总结:defer rows.Close() 是 Go 中处理数据库结果集资源释放的标准范式——它提升代码健壮性、增强可维护性,并与 Go 的“明确即安全”哲学高度契合。始终将其作为 db.Query / db.QueryRow 后的第二步操作(在错误检查之后),即可有效规避连接泄漏风险。










