每次Golang上的全局数据库连接和打开连接之间的性能差异

时间:2017-06-15 08:53:16

标签: sql performance go connection-pooling

在我当前的项目中,每次用户发出请求时我都会打开一个新的数据库连接。例如:

func login(w http.ResponseWriter, r *http.Request) {

...

db, err := sqlx.Connect("postgres", "user=postgres password=*** dbname=postgres")

if err != nil {
    ErrorWithJSON(w, err.Error(), http.StatusBadRequest)
    return
}

db.SetMaxIdleConns(0)
db.SetConnMaxLifetime(time.Second * 30)

user, err := loginManager(db, m)

...

err = db.Close()

}

当我搜索其他人的代码时,我发现大多数开发人员为数据库连接创建了一个全局变量,将其设置在main上并在整个项目中使用此变量。

我想知道这些方法之间有什么区别吗?如果我使用全局变量,当5个不同的用户发出注册/登录请求等时会有任何延迟。如果有延迟,我应该创建多个数据库连接并将它们存储在切片中以备将来请求,这样我就可以随机选择用户提出要求。就像一个简单的负载均衡器,我不知道?

抱歉有多个问题。谢谢!

1 个答案:

答案 0 :(得分:2)

Yes, there might be a huge performance difference (might be several order of magnitude depending on the nature of queries you run and on system and server configuration).

The sqlx.DB type wraps (embeds) an sql.DB type, which manages a pool of connections:

DB is a database handle representing a pool of zero or more underlying connections. It's safe for concurrent use by multiple goroutines.

The sql package creates and frees connections automatically; it also maintains a free pool of idle connections. If the database has a concept of per-connection state, such state can only be reliably observed within a transaction.

Every time you open a new connection, a lot of things have to happen in the "background": connection string has to be parsed, a TCP connection has to be estabilished, authentication / authorization must be performed, resources must be allocated at both sides (client and server) etc. These are just the main, obvious things. Even though some of these may be provided / implemented optimized, cached, there is still a significant overhead compared to having a single DB instance which might have multiple established, authenticated connections ready in a pool, waiting to be used / utilized.

Also quoting from sql.Open():

The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB.

sqlx.Connect() which you used calls sqlx.Open() which is "the same as sql.Open, but returns an *sqlx.DB instead".

So all in all, use a single, global sqlx.DB or sql.DB instance, and share / use that everywhere. It provides you automatic connection- and connection pool management. This will provide you the best performance. You may fine-tune the connection pool with the DB.SetConnMaxLifetime(), DB.SetMaxIdleConns() and DB.SetMaxOpenConns() methods.

Idle connections (DB.SetMaxIdleConns()) are those that are not in-use currently, but sitting in the pool, waiting for someone to pick them up. You should definitely have some of these, e.g. 5 or 10 of them, or even more. DB.SetConnMaxLifetime() controls how long a new connection may be used. Once it grows older than this, it will be closed (and a new one will be opened if needed). You shouldn't change this, default behavior is never to expire connections. Basically all defaults are sensible, you should only play with them if you experience performance problems. Also, read docs of these methods to have a clear picture.

See this similar, possible duplicate question:

mgo - query performance seems consistently slow (500-650ms)