英文:
Golang database/sql hangs with SetMaxOpenConns
问题
我已经复制了一个最小可复现的测试代码。简而言之,当SetMaxOpenConns
设置为10时,程序在10次后永远挂起。我找到了一个相关的线程,但它似乎已经解决和测试过了:https://github.com/golang/go/issues/6593
请注意,通过注释掉SetMaxOpenConns
,代码可以正常运行。
我做错了什么?还是我应该提一个新的问题?
1 package main
2
3 import (
4 "database/sql"
5 "log"
6 "time"
7 _ "github.com/lib/pq"
8 )
9
10 func main(){
11 // 建立数据库连接
12 db, err := sql.Open("postgres", "host=0.0.0.0 port=5432 user=postgres password=password dbname=test sslmode=disable")
13 if err != nil {
14 log.Fatal(err)
15 }
16
17 db.SetMaxOpenConns(10) // 注释掉这一行将解决问题
18 db.SetMaxIdleConns(10)
19 db.SetConnMaxLifetime(10 * time.Second)
20
21 // 查询超过最大连接数;请注意,这将永远挂起
22 for i:=0; i<12; i++ {
23 rows, err := Query(db)
24 if err != nil {
25 log.Fatal(err)
26 }
27 log.Println(i)
28 log.Println(rows)
29 }
30 }
31
32 func Query(db *sql.DB) (*sql.Rows, error){
33 stmt, err := db.Prepare("SELECT * FROM test;")
34 if err != nil {
35 log.Fatal(err)
36 }
37
38 defer stmt.Close()
39
40 rows, err := stmt.Query()
41 if err != nil {
42 log.Fatal(err)
43 }
44
45 return rows, nil
46 }
英文:
I have pasted a minimally reproducible test code. In short, with SetMaxOpenConns
set to 10, the program hangs forever after 10. I found this relevant thread from way back when, but it seems resolved and tested: https://github.com/golang/go/issues/6593
Note that by commenting out the SetMaxOpenConns
the code runs normally.
What am I doing wrong? or should I open a new issue?
1 package main
2
3 import (
4 "database/sql"
5 "log"
6 "time"
7 _ "github.com/lib/pq"
8 )
9
10 func main(){
11 // Establish db connection
12 db, err := sql.Open("postgres", "host=0.0.0.0 port=5432 user=postgres password=password dbname=test sslmode=disable")
13 if err != nil {
14 log.Fatal(err)
15 }
16
17 db.SetMaxOpenConns(10) // commenting this line will resolve the problem
18 db.SetMaxIdleConns(10)
19 db.SetConnMaxLifetime(10 * time.Second)
20
21 // Query more than max open; note that hangs forever
22 for i:=0; i<12; i++ {
23 rows, err := Query(db)
24 if err != nil {
25 log.Fatal(err)
26 }
27 log.Println(i)
28 log.Println(rows)
29 }
30 }
31
32 func Query(db *sql.DB) (*sql.Rows, error){
33 stmt, err := db.Prepare("SELECT * FROM test;")
34 if err != nil {
35 log.Fatal(err)
36 }
37
38 defer stmt.Close()
39
40 rows, err := stmt.Query()
41 if err != nil {
42 log.Fatal(err)
43 }
44
45 return rows, nil
46 }
答案1
得分: 4
你需要使用rows.Next
完全迭代结果集,或者调用rows.Close()
;根据文档的说明:
>Close方法关闭Rows,防止进一步枚举。如果调用Next方法返回false并且没有更多的结果集,Rows会自动关闭,只需检查Err的结果即可。Close是幂等的,不会影响Err的结果。
示例代码如下:
for i:=0; i<12; i++ {
rows, err := Query(db)
if err != nil {
log.Fatal(err)
}
log.Println(i)
log.Println(rows)
if err = rows.Close(); err != nil {
panic(err)
}
}
要使其有用,你需要遍历行(请参阅文档中的示例)。
数据库连接将保持使用状态,直到结果集关闭(此时将返回到连接池)。因为你在循环中执行这个操作,所以最终会有10个活动的结果集,当你再次调用Query()
时,sql
包将等待一个可用的连接(这将永远不会发生)。
请注意,由于你的查询没有参数(并且只使用了一次stmt
),调用Prepare
没有好处;以下代码更简单,但结果相同:
func Query(db *sql.DB) (*sql.Rows, error) {
return db.Query("SELECT * FROM test;")
}
英文:
You need to either fully iterate through the result set with rows.Next
and/or call rows.Close()
; as per the docs:
>Close closes the Rows, preventing further enumeration. If Next is called and returns false and there are no further result sets, the Rows are closed automatically and it will suffice to check the result of Err. Close is idempotent and does not affect the result of Err.
Something like:
for i:=0; i<12; i++ {
rows, err := Query(db)
if err != nil {
log.Fatal(err)
}
log.Println(i)
log.Println(rows)
if err = rows.Close(); err != nil {
panic(err)
}
}
For this to be useful you need to iterate through the rows (see the example in the docs).
The connection to the database will remain in use until the result set is closed (at which point it is returned to the pool). Because you are doing this in a loop you will end up with 10 active result sets and when you call Query()
again the sql
package will wait for a connection to become available (which will never happen).
Note that because your query has no parameters (and you are only using the stmt
once) calling Prepare
has no benefit; the following is simpler and will have the same result:
func Query(db *sql.DB) (*sql.Rows, error) {
return db.Query("SELECT * FROM test;")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论