使用database/sql查询比直接查询数据库慢很多
我使用 golang 应用程序并使用 psql 对本地 postgresql 实例运行相同的查询。时间差异很大,我想知道为什么。使用explain/analyze查询花费了1ms,在golang中使用database/sql,花费了24ms。我在下面添加了我的代码片段。我意识到解释/分析可能并不等同于直接查询数据库,并且可能还涉及一些网络延迟,但是差异仍然很大。为什么会出现这样的差异呢?
编辑:我已经尝试过使用 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
I'm running the same query against a local postgresql instance using a golang application, and using psql
. The timings differ greatly and I'm wondering why. Using explain/analyze the query took 1ms, using database/sql
in golang, it took 24ms. I've added my code snippets below. I realize that explain/analyze may not be equivalent to querying the database directly, and there may be some network latency involved as well, however the discrepancy is still significant. Why is there such a discrepancy?
edit: I've tried the above with a sample size of 10+ queries, and the discrepancy still holds true.
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
注意#1:
sql.DB
< /a> 不代表一个连接,而是代表一个池连接。注意#2:
sql.Open
< /a> 初始化池,但它不必实际打开连接,它只允许验证 dsn 输入,然后连接的打开将由池延迟处理。您的 1st
db.Query
速度慢的原因是您从一个新的连接池开始,该连接池有 0 个空闲(但打开)的连接,因此1stdb.Query
需要首先与服务器建立新的连接,然后才能执行 sql 语句。2nd
db.Query
速度也很慢的原因是 1stdb.Query
创建的连接已尚未释放回池中,因此您的 2nddb.Query
还需要首先与服务器建立新连接,然后才能执行 sql 语句。要将连接释放回池,您需要首先保留 db.Query 的主要返回值,然后调用
Close
方法就可以了。要从至少有一个可用连接的池开始,请调用
Ping< /code>
初始化池后。
示例:
我的机器上的时间(没有
db.Ping
只有 #0 很慢)我的机器上的时间(有
db.Ping
#0 比没有时快很多)A关于准备好的语句的注意事项:
如果您正在执行一个不带参数的简单查询,例如
db.Query("select 1 where true")
,那么您实际上只是在执行一个简单的查询。但是,如果您正在使用参数执行查询,例如
db.Query("select 1 where $1", true)
,那么实际上您正在创建并执行准备好的语句。请参阅4.2。值表达式,它说:
还有 位置参数 说:
postgres 的消息流协议如何指定
简单查询
和扩展查询
最后,在
lib/pq
驱动程序:Note #1:
sql.DB
does not represent a connection, instead it represents a pool of connections.Note #2:
sql.Open
initializes the pool but it does not have to actually open a connection, it's allowed to only validate the dsn input and then the opening of the connections will be handled lazily by the pool.The reason your 1st
db.Query
is slow is because you're starting off with a fresh connection pool, one that has 0 idle (but open) connections, and therefore the 1stdb.Query
will need to first establish a new connection to the server and only after that will it be able to execute the sql statement.The reason your 2nd
db.Query
is also slow is because the connection created by the 1stdb.Query
has not been released back to the pool, and therefore your 2nddb.Query
will also need to first establish a new connection to the server before it can execute the sql statement.To release a connection back to the pool you need to first retain the primary return value of
db.Query
and then invoke theClose
method on it.To start off with a pool that has at least one available connection, call
Ping
right after initializing the pool.Example:
Times on my machine (without
db.Ping
only #0 is slow)Times on my machine (with
db.Ping
#0 is a lot faster than without)A note on prepared statements:
If you're executing a simple query with no arguments e.g.
db.Query("select 1 where true")
then you really are executing just a simple query.If, however, you're executing a query with arguments e.g.
db.Query("select 1 where $1", true)
then, in actuality, you are creating and executing a prepared statement.See 4.2. Value Expressions, it says:
Also Positional Parameters says:
How the postgres' message-flow protocol specifies
simple queries
andextended queries
And finally, under the covers of the
lib/pq
driver: