亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

使用 `database/sql` 查詢比直接查詢數據庫要慢得多

使用 `database/sql` 查詢比直接查詢數據庫要慢得多

Go
慕萊塢森 2022-07-11 16:48:52
我正在使用 golang 應用程序對本地 postgresql 實例運行相同的查詢,并使用psql. 時間差別很大,我想知道為什么。使用解釋/分析查詢需要 1 毫秒,database/sql在 golang 中使用需要 24 毫秒。我在下面添加了我的代碼片段。我意識到解釋/分析可能不等同于直接查詢數據庫,并且可能還涉及一些網絡延遲,但是差異仍然很大。為什么會有這樣的差異?編輯:我已經用 10 多個查詢的樣本大小嘗試了上述方法,但差異仍然存在。postgres=# \timingTiming is on.postgres=# select 1; ?column?----------        1(1 row)Time: 2.456 mspostgres=# 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 mspackage 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.981435mssecond query took 13.343845ms
查看完整描述

1 回答

?
holdtom

TA貢獻1805條經驗 獲得超10個贊

注意#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() {

    // setup database connection

    db, err := sql.Open("postgres", "postgres:///?sslmode=disable")

    if err != nil {

        panic(err)

    } else if err := db.Ping(); err != nil {

        panic(err)

    }


    for i := 0; i < 5; i++ {

        // query database

        firstQueryStart := time.Now()

        rows, err := db.Query("select 1;")

        firstQueryEnd := time.Now()

        if err != nil {

            panic(err)

        }


        // put the connection back to the pool so

        // that it can be reused by next iteration

        rows.Close()


        fmt.Println(fmt.Sprintf("query #%d took %s", i, firstQueryEnd.Sub(firstQueryStart).String()))

    }

}

我的機器上的時間(db.Ping只有 #0 很慢)


query #0 took 6.312676ms

query #1 took 102.88μs

query #2 took 66.702μs

query #3 took 64.694μs

query #4 took 208.016μs

我的機器上的時間(db.Ping#0 比沒有快很多)


query #0 took 284.846μs

query #1 took 78.349μs

query #2 took 76.518μs

query #3 took 81.733μs

query #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司機的掩護下:


    ...


    // Check to see if we can use the "simpleQuery" interface, which is

    // *much* faster than going through prepare/exec

    if len(args) == 0 {

        return cn.simpleQuery(query)

    }


    if cn.binaryParameters {

        cn.sendBinaryModeQuery(query, args)


        cn.readParseResponse()

        cn.readBindResponse()

        rows := &rows{cn: cn}

        rows.rowsHeader = cn.readPortalDescribeResponse()

        cn.postExecuteWorkaround()

        return rows, nil

    }

    st := cn.prepareTo(query, "")

    st.exec(args)

    return &rows{

        cn:         cn,

        rowsHeader: st.rowsHeader,

    }, nil


    ...


查看完整回答
反對 回復 2022-07-11
  • 1 回答
  • 0 關注
  • 202 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號