一尘不染

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

go

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

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

}

当我搜索其他人的代码时,我发现大多数开发人员都会为数据库连接创建一个全局变量,在全局变量上进行设置,然后在整个项目中使用该变量。

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

抱歉,有多个问题。谢谢!


阅读 645

收藏
2020-07-02

共1个答案

一尘不染

是的,可能会有巨大的性能差异(取决于您运行的查询的性质以及系统和服务器配置,可能有几个数量级)。

sqlx.DB类型包装(嵌入)一个sql.DB类型,该类型管理连接池:

DB是代表零个或多个基础连接池的数据库句柄。对于多个goroutine并发使用是安全的。

sql软件包会自动创建并释放连接。它还维护空闲连接的空闲池。如果数据库具有每个连接状态的概念,则只能在事务中可靠地观察到这种状态。

每次打开新连接时,“背景”中都会发生很多事情:必须解析连接字符串,必须建立TCP连接,必须执行身份验证/授权,必须在两侧分配资源(客户端和服务器)等。这些只是主要的,显而易见的内容。即使可以优化/缓存来提供/实现其中一些功能,但是与拥有一个DB实例(可能在池中准备好多个已建立的经过身份验证的连接,等待使用/利用)的单个实例相比,仍然存在大量开销。

也引用自sql.Open()

返回的数据库可安全地供多个goroutine并发使用,并维护其自己的空闲连接池。 因此,Open函数应仅被调用一次。 很少需要关闭数据库。

sqlx.Connect()您使用的电话sqlx.Open()
“同sql.Open,但返回的* sqlx.DB代替”

因此,总而言之,请使用一个全局sqlx.DBsql.DB实例,并在任何地方共享/使用它。它为您提供自动连接和连接池管理。这将为您提供最佳性能。您可以微调与连接池DB.SetConnMaxLifetime()DB.SetMaxIdleConns()DB.SetMaxOpenConns()方法。

空闲连接(DB.SetMaxIdleConns())是当前未使用但坐在游泳池中,等待有人将其捡起的那些连接。您绝对应该拥有其中的一些,例如5或10,甚至更多。DB.SetConnMaxLifetime()控制使用新连接的时间。一旦它变老了,它将被关闭(如果需要,将打开一个新的)。您不应该更改此设置,默认行为是永不终止连接。基本上所有默认设置都是明智的,只有在遇到性能问题时才应使用它们。另外,请阅读这些方法的文档以清楚了解。

2020-07-02