Ali*_*xel 7 memory sqlite memory-leaks out-of-memory go
我有问题让Go与SQLite很好地玩,我过去做过没有问题,但它已经有一段时间了,我不记得我做了什么让它正常工作.我正在使用Go和mattn/go-sqlite3软件包一起处理并将大量数据插入到SQLite数据库中,但不知怎的,Go总是最终吃掉我所有的RAM,直到最后退出时出现错误状态代码.为了确保我已经将内存饥饿问题隔离到SQLite,我编写了以下简单程序来测试它:
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./test.db"); if err != nil {
log.Fatal(err)
}; defer db.Close()
ddl := `
PRAGMA automatic_index = ON;
PRAGMA cache_size = 32768;
PRAGMA cache_spill = OFF;
PRAGMA foreign_keys = ON;
PRAGMA journal_size_limit = 67110000;
PRAGMA locking_mode = NORMAL;
PRAGMA page_size = 4096;
PRAGMA recursive_triggers = ON;
PRAGMA secure_delete = ON;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;
PRAGMA journal_mode = WAL;
PRAGMA wal_autocheckpoint = 16384;
CREATE TABLE IF NOT EXISTS "user" (
"id" TEXT,
"username" TEXT,
"password" TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS "id" ON "user" ("id");
`
_, err = db.Exec(ddl); if err != nil {
log.Fatal(err)
}
queries := map[string]*sql.Stmt{}
queries["user"], _ = db.Prepare(`INSERT OR REPLACE INTO "user" VALUES (?, ?, ?);`); if err != nil {
log.Fatal(err)
}; defer queries["user"].Close()
tx, err := db.Begin(); if err != nil {
log.Fatal(err)
}
for i := 0; i < 10000000; i++ {
user := map[string]string{
"id": string(i),
"username": "foo",
"password": "bar",
}
_, err := tx.Stmt(queries["user"]).Exec(user["id"], user["username"], user["password"]); if err != nil {
log.Fatal(err)
}
if i % 32768 == 0 {
tx.Commit()
db.Exec(`PRAGMA shrink_memory;`)
tx, err = db.Begin(); if err != nil {
log.Fatal(err)
}
fmt.Println(i)
}
}
tx.Commit()
}
Run Code Online (Sandbox Code Playgroud)
当我运行上面的代码,围棋吃超过100 MIB的记忆每一秒每没有任何释放,一分钟后,或因此结束了耗时6/7吉布然后程序就会被杀死.我已经尝试了有和没有定义SQLite PRAGMA的变体,但没有运气.
根据定义的PRAGMA,SQLite永远不应该使用超过128 MiB的RAM.
我是否犯了任何错误或者使用mattn/go-sqlite3或Go GC有什么问题?
与剖析davecheney/profile按照这些指令产生这种不那么有用的输出:
alix@900X4C:~/Go/src$ go tool pprof --text ./test /tmp/profile102098478/mem.pprof
Adjusting heap profiles for 1-in-4096 sampling rate
Total: 0.0 MB
0.0 100.0% 100.0% 0.0 100.0% runtime.allocm
0.0 0.0% 100.0% 0.0 100.0% database/sql.(*DB).Exec
0.0 0.0% 100.0% 0.0 100.0% database/sql.(*DB).conn
0.0 0.0% 100.0% 0.0 100.0% database/sql.(*DB).exec
0.0 0.0% 100.0% 0.0 100.0% github.com/mattn/go-sqlite3.(*SQLiteDriver).Open
0.0 0.0% 100.0% 0.0 100.0% github.com/mattn/go-sqlite3._Cfunc_sqlite3_threadsafe
0.0 0.0% 100.0% 0.0 100.0% main.main
0.0 0.0% 100.0% 0.0 100.0% runtime.cgocall
0.0 0.0% 100.0% 0.0 100.0% runtime.gosched0
0.0 0.0% 100.0% 0.0 100.0% runtime.main
0.0 0.0% 100.0% 0.0 100.0% runtime.newextram
Run Code Online (Sandbox Code Playgroud)
这只是1000000次迭代,内存仍然像没有明天一样增长.
我还在两个MacBook Pro上尝试了相同的代码,两个都运行最新版本的Go from brew(1.3.1),其中一个内存使用量超过10 GiB,另一个平均为2 GiB的RAM消耗.这看起来像一个奇怪的行为,我可以做些什么来追查差异并修复内存占用?
我无法重现您的结果.它使用大约100 MiB的内存.
$ go version
go version devel +7ab3adc146c9 Sun Oct 19 10:33:50 2014 -0700 linux/amd64
$ sqlite3 --version
3.8.2 2013-12-06 14:53:30 27392118af4c38c5203a04b8013e1afdb1cebd0d
$ go get -v github.com/mattn/go-sqlite3
github.com/mattn/go-sqlite3 (download)
github.com/mattn/go-sqlite3
$ go run simple.go
0
32768
65536
<SNIP>
9928704
9961472
9994240
$
Run Code Online (Sandbox Code Playgroud)
runtime.MemStats记录有关Go内存分配器的统计信息.它不包括SQLite管理的内存.例如,在程序结束时,
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
fmt.Println(
ms.Alloc, // bytes allocated and still in use
ms.TotalAlloc, // bytes allocated (even if freed)
ms.Sys, // bytes obtained from system (sum of XxxSys below)
ms.Mallocs, // number of mallocs
ms.Frees, // number of frees
)
Run Code Online (Sandbox Code Playgroud)
输出:
12161440 7953059928 18757880 160014535 159826250
Run Code Online (Sandbox Code Playgroud)
它也适用于Go 1.4 Beta 1
$ go version go version go1.4beta1 linux/amd64