使用 `database/sql` 查询比直接查询数据库要慢得多

我正在使用 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

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()))

    }

first query took 13.981435ms

second query took 13.343845ms


慕莱坞森
浏览 174回答 1
1回答

holdtom

注意#1:sql.DB不代表连接,而是代表连接池。注意#2:sql.Open初始化池,但它不必实际打开连接,只允许验证 dsn 输入,然后连接的打开将由池延迟处理。你的第 db.Query一个慢的原因是因为你从一个新的连接池开始,一个有 0 个空闲(但打开)连接的连接池,因此第一个 db.Query需要首先建立与服务器的新连接,然后才会它能够执行sql语句。你的第二个 db.Query也很慢的原因是因为第一个创建的连接还 db.Query没有释放回池中,因此你的第二个 db.Query也需要先与服务器建立新的连接才能执行sql语句。要释放与池的连接,您需要首先保留 的主要返回值,db.Query然后调用其Close上的方法。要从至少有一个可用连接的池开始,请Ping在初始化池后立即调用。例子:func main() {&nbsp; &nbsp; // setup database connection&nbsp; &nbsp; db, err := sql.Open("postgres", "postgres:///?sslmode=disable")&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; panic(err)&nbsp; &nbsp; } else if err := db.Ping(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; panic(err)&nbsp; &nbsp; }&nbsp; &nbsp; for i := 0; i < 5; i++ {&nbsp; &nbsp; &nbsp; &nbsp; // query database&nbsp; &nbsp; &nbsp; &nbsp; firstQueryStart := time.Now()&nbsp; &nbsp; &nbsp; &nbsp; rows, err := db.Query("select 1;")&nbsp; &nbsp; &nbsp; &nbsp; firstQueryEnd := time.Now()&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; panic(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // put the connection back to the pool so&nbsp; &nbsp; &nbsp; &nbsp; // that it can be reused by next iteration&nbsp; &nbsp; &nbsp; &nbsp; rows.Close()&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(fmt.Sprintf("query #%d took %s", i, firstQueryEnd.Sub(firstQueryStart).String()))&nbsp; &nbsp; }}我的机器上的时间(db.Ping只有 #0 很慢)query #0 took 6.312676msquery #1 took 102.88µsquery #2 took 66.702µsquery #3 took 64.694µsquery #4 took 208.016µs我的机器上的时间(db.Ping#0 比没有快很多)query #0 took 284.846µsquery #1 took 78.349µsquery #2 took 76.518µsquery #3 took 81.733µsquery #4 took 103.862µs关于准备好的陈述的说明:例如,如果您正在执行一个不带参数的简单查询,db.Query("select 1 where true")那么您实际上只是在执行一个简单查询。但是,如果您正在执行带有参数的查询,例如db.Query("select 1 where $1", true),那么实际上,您正在创建和执行准备好的语句。见4.2。值表达式,它说:值表达式是以下之一: ...位置参数引用,在函数定义或准备好的语句的主体中......位置参数也说:位置参数引用用于指示从外部提供给 SQL 语句的值。参数用于 SQL 函数定义和准备好的查询中。一些客户端库还支持独立于 SQL 命令字符串指定数据值,在这种情况下,参数用于引用线外数据值。postgres 的消息流协议如何指定simple queries和extended queries扩展查询协议将上述简单查询协议分解为多个步骤。准备步骤的结果可以多次重复使用以提高效率。此外, 还提供了其他功能,例如可以将数据值作为单独的参数提供,而不必将它们直接插入到查询字符串中。最后,在lib/pq司机的掩护下:&nbsp; &nbsp; ...&nbsp; &nbsp; // Check to see if we can use the "simpleQuery" interface, which is&nbsp; &nbsp; // *much* faster than going through prepare/exec&nbsp; &nbsp; if len(args) == 0 {&nbsp; &nbsp; &nbsp; &nbsp; return cn.simpleQuery(query)&nbsp; &nbsp; }&nbsp; &nbsp; if cn.binaryParameters {&nbsp; &nbsp; &nbsp; &nbsp; cn.sendBinaryModeQuery(query, args)&nbsp; &nbsp; &nbsp; &nbsp; cn.readParseResponse()&nbsp; &nbsp; &nbsp; &nbsp; cn.readBindResponse()&nbsp; &nbsp; &nbsp; &nbsp; rows := &rows{cn: cn}&nbsp; &nbsp; &nbsp; &nbsp; rows.rowsHeader = cn.readPortalDescribeResponse()&nbsp; &nbsp; &nbsp; &nbsp; cn.postExecuteWorkaround()&nbsp; &nbsp; &nbsp; &nbsp; return rows, nil&nbsp; &nbsp; }&nbsp; &nbsp; st := cn.prepareTo(query, "")&nbsp; &nbsp; st.exec(args)&nbsp; &nbsp; return &rows{&nbsp; &nbsp; &nbsp; &nbsp; cn:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;cn,&nbsp; &nbsp; &nbsp; &nbsp; rowsHeader: st.rowsHeader,&nbsp; &nbsp; }, nil&nbsp; &nbsp; ...
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go