// QueryUserFileMetas : 批量獲取用戶文件信息
func QueryUserFileMetas(username string, limit int) ([]UserFile, error) {stmt, err := mydb.DBConn().Prepare("select file_sha1,file_name,file_size,upload_at," +"last_update from tbl_user_file where user_name=? limit ?")if err != nil {return nil, err}defer stmt.Close()rows, err := stmt.Query(username, limit)if err != nil {return nil, err}var userFiles []UserFilefor rows.Next() {ufile := UserFile{}err = rows.Scan(&ufile.FileHash, &ufile.FileName, &ufile.FileSize,&ufile.UploadAt, &ufile.LastUpdated)if err != nil {fmt.Println(err.Error())break}userFiles = append(userFiles, ufile)}return userFiles, nil
}
這里的stmt需要手動回收,rows需要嗎?
對象 | 是否需要手動關閉 | 為什么 |
---|---|---|
stmt (預處理語句) | ? 需要 | 占用數據庫連接資源,必須關閉釋放資源 |
rows (查詢結果集) | ? 也需要 | 持有數據庫游標或連接,必須關閉釋放資源 |
🔍 原因詳解
🔹1. stmt.Close()
stmt 是通過 Prepare() 創建的預編譯語句句柄。
它通常會綁定數據庫連接資源,即使查詢結束也不會自動釋放。
不關閉會導致連接池資源泄漏,最終使數據庫拒絕新的連接。
🔹2. rows.Close()
rows 是執行查詢后返回的結果集,底層其實是一個游標。
它也持有數據庫連接,直到你顯式地關閉或完全遍歷完所有行。
如果不 Close():
連接不會釋放給連接池(可能一直掛起)
資源泄漏:數據庫連接數會越來越多,直到超出限制
? 總結
stmt.Close() 和 rows.Close() 都必須顯式調用,通常用 defer 來管理。
否則會引起連接池資源耗盡、數據庫連接泄漏等問題。
Go 的數據庫接口(database/sql)設計得很靈活,但需要你手動管理生命周期。
疑問?為什么不會隨著函數的調用結束被內存自動回收了呢?
🧠 背后原理:內存釋放 ≠ 資源釋放
Go 的垃圾回收器(GC)能做什么?
GC 只能自動清理內存對象(如你創建的結構體、切片、字符串等),前提是這些對象不再被引用。
GC 無法自動釋放 “非內存資源”,比如:
- 網絡連接(TCP socket).
- 數據庫連接
- 文件句柄
- goroutine(除非主動退出)
- 系統級游標、IO句柄等
? Go GC 是“內存管理器”,而非“資源管理器”。
🔍 對比:內存 vs 非內存資源
類型 | 例子 | GC 能自動釋放? | 需要手動釋放? |
---|---|---|---|
內存對象 | []byte , string , map | ? 是 | ? 否 |
數據庫連接 | rows , stmt , tx | ? 否 | ? 是 |
網絡連接 | net.Conn | ? 否 | ? 是 |
文件句柄 | os.File | ? 否 | ? 是 |
Go 的垃圾回收器只會自動釋放內存,但不會自動關閉文件、數據庫連接等 “非內存資源”。這些資源你必須手動關閉。