使用database/sql查询比直接查询数据库慢很多

Anu*_*g M 3 postgresql go psql

我使用 golang 应用程序对本地 postgresql 实例运行相同的查询,并使用psql. 时间差异很大,我想知道为什么。使用解释/分析查询花费了 1 毫秒,database/sql在 golang 中使用,花费了 24 毫秒。我在下面添加了我的代码片段。我意识到解释/分析可能并不等同于直接查询数据库,并且可能还涉及一些网络延迟,但是差异仍然很大。为什么会出现这样的差异呢?

编辑:我已经尝试过使用 10 个以上查询的样本大小进行上述操作,并且差异仍然存在。

postgres=# \timing
Timing is on.
postgres=# select 1;
 ?column?
----------
        1
(1 row)

Time: 2.456 ms
postgres=# explain analyze select 1;
                                     QUERY PLAN
------------------------------------------------------------------------------------
 Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.002..0.002 rows=1 loops=1)
 Planning Time: 0.017 ms
 Execution Time: 0.012 ms
(3 rows)

Time: 3.748 ms
Run Code Online (Sandbox Code Playgroud)
package main

    import (
        "database/sql"
        "fmt"
        _ "github.com/lib/pq"
        "time"
    )

    func main() {
        // setup database connection
        db, err := sql.Open("postgres", "host='localhost' port=5432 user='postgres' password='' dbname='postgres' sslmode=disable")
        if err != nil {
            panic(err)
        }

        // query database
        firstQueryStart := time.Now()
        _, err = db.Query("select 1;")
        firstQueryEnd := time.Now()
        if err != nil {
            panic(err)
        }
        fmt.Println(fmt.Sprintf("first query took %s", firstQueryEnd.Sub(firstQueryStart).String()))

        //run the same query a second time and measure the timing
        secondQueryStart := time.Now()
        _, err = db.Query("select 1;")
        secondQueryEnd := time.Now()
        if err != nil {
            panic(err)
        }
        fmt.Println(fmt.Sprintf("second query took %s", secondQueryEnd.Sub(secondQueryStart).String()))
    }
Run Code Online (Sandbox Code Playgroud)
first query took 13.981435ms
second query took 13.343845ms
Run Code Online (Sandbox Code Playgroud)

mko*_*iva 7

注意#1sql.DB不代表连接,而是代表连接池。

\n

注意#2sql.Open初始化池,但不必实际打开连接,它只允许验证 dsn 输入,然后连接的打开将由池延迟处理。

\n

第一个 缓慢的原因db.Query是因为您从一个新的连接池开始,该连接池有 0 个空闲(但打开)连接,因此第一个 db.Query需要首先建立到服务器的新连接,并且只有在此之后是否能够执行sql语句。

\n

你的第二个 也很慢的原因是因为第一个db.Query创建的连接尚未释放回池中,因此你的第二个也需要先建立一个到服务器的新连接,然后才能执行sql语句。 db.Query db.Query

\n
\n

要将连接释放回池,您需要首先保留其主要返回值db.Query,然后调用Close其方法。

\n

要从至少有一个可用连接的池开始,请Ping在初始化池后立即调用。

\n
\n

例子:

\n
func main() {\n    // setup database connection\n    db, err := sql.Open("postgres", "postgres:///?sslmode=disable")\n    if err != nil {\n        panic(err)\n    } else if err := db.Ping(); err != nil {\n        panic(err)\n    }\n\n    for i := 0; i < 5; i++ {\n        // query database\n        firstQueryStart := time.Now()\n        rows, err := db.Query("select 1;")\n        firstQueryEnd := time.Now()\n        if err != nil {\n            panic(err)\n        }\n\n        // put the connection back to the pool so\n        // that it can be reused by next iteration\n        rows.Close()\n\n        fmt.Println(fmt.Sprintf("query #%d took %s", i, firstQueryEnd.Sub(firstQueryStart).String()))\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我的机器上的时间(没有db.Ping#0 很慢)

\n
query #0 took 6.312676ms\nquery #1 took 102.88\xc2\xb5s\nquery #2 took 66.702\xc2\xb5s\nquery #3 took 64.694\xc2\xb5s\nquery #4 took 208.016\xc2\xb5s\n
Run Code Online (Sandbox Code Playgroud)\n

在我的机器上的时间(使用db.Ping#0 比不使用 #0 快很多)

\n
query #0 took 284.846\xc2\xb5s\nquery #1 took 78.349\xc2\xb5s\nquery #2 took 76.518\xc2\xb5s\nquery #3 took 81.733\xc2\xb5s\nquery #4 took 103.862\xc2\xb5s\n
Run Code Online (Sandbox Code Playgroud)\n
\n

关于准备好的声明的注释:

\n

如果您正在执行一个不带参数的简单查询,db.Query("select 1 where true")那么您实际上只是在执行一个简单的查询。

\n

但是,如果您正在执行带参数的查询,那么db.Query("select 1 where $1", true)实际上您正在创建并执行准备好的语句。

\n

4.2。值表达式,它说:

\n
\n

值表达式是以下之一:...

\n
    \n
  • 函数定义或预备语句主体中的位置参数引用
    \n...
  • \n
\n
\n

位置参数还说:

\n
\n

位置参数引用用于指示从外部提供给 SQL 语句的值。参数用于 SQL\n函数定义和准备好的查询中。某些客户端库还支持与 SQL 命令字符串分开指定数据值,在这种情况下,参数用于引用外联数据值。

\n
\n

postgres 的消息流协议如何指定simple queriesextended queries

\n
\n

扩展查询协议将上述简单查询协议分解为多个步骤。准备步骤的结果\n可以多次重复使用以提高效率。此外,还可以使用其他功能,例如可以将数据值作为单独的参数提供,而不必将它们直接插入查询字符串中。

\n
\n

最后,在lib/pq司机的掩护下:

\n
query #0 took 6.312676ms\nquery #1 took 102.88\xc2\xb5s\nquery #2 took 66.702\xc2\xb5s\nquery #3 took 64.694\xc2\xb5s\nquery #4 took 208.016\xc2\xb5s\n
Run Code Online (Sandbox Code Playgroud)\n