
使用 `defer rows.close()` 可以确保查询结果集在函数返回前自动关闭,避免因遗漏调用导致连接泄漏,是 go 中推荐的标准实践。
在 Go 的数据库操作中,*sql.Rows 对象代表一个查询结果集,它底层持有一个数据库连接(或连接池中的会话)。虽然 rows.Close() 并不直接关闭物理连接(连接通常由连接池复用),但必须显式调用它——否则该结果集会持续占用连接资源,阻碍连接归还给池,最终可能导致 too many connections 或 i/o timeout 等错误。
最简洁、安全且符合 Go 习惯的写法是结合 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.Close()✅ 优势说明:
- 可靠性:defer 保证 Close() 总被执行,即使循环中 return、panic 或发生未捕获错误;
- 可读性:defer rows.Close() 紧邻 db.Query(),语义清晰,符合“打开即清理”的直觉;
- 一致性:与 os.Open() + defer f.Close()、sql.Tx + defer tx.Rollback() 等惯用法统一。
⚠️ 注意事项:
- defer 在函数作用域结束时触发,而非代码块(如 for 或 if)结束时。因此不要在循环内重复 defer rows.Close()(这会导致多次 defer 同一对象,且仅最后一次生效);
- 若需在 for rows.Next() 循环中途提前退出并释放资源,defer 依然有效——它不会被跳过;
- rows.Close() 是幂等的(多次调用无副作用),但无必要主动重复调用;
- 更进一步,对于只读单行查询,优先使用 db.QueryRow().Scan(),它内部自动处理关闭,无需手动管理 Rows。
总结:defer rows.Close() 不仅是更短的写法,更是 Go “明确资源生命周期”设计哲学的体现——让清理逻辑紧贴资源获取,兼顾安全、简洁与可维护性。










