Golang的database/sql在设置SetMaxOpenConns时出现了卡住的情况。

huangapple go评论78阅读模式
英文:

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     &quot;database/sql&quot;
   5     &quot;log&quot;
   6     &quot;time&quot;
   7     _ &quot;github.com/lib/pq&quot;
   8 )
   9
  10 func main(){
  11     // Establish db connection
  12     db, err := sql.Open(&quot;postgres&quot;, &quot;host=0.0.0.0 port=5432 user=postgres password=password dbname=test sslmode=disable&quot;)
  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&lt;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(&quot;SELECT * FROM test;&quot;)
  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&lt;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&lt;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(&quot;SELECT * FROM test;&quot;)
}

huangapple
  • 本文由 发表于 2022年7月29日 07:54:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/73160295.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定