Skip to content

golang sql 连接超时探究 #22

Description

@snxq

前不久的一次线上数据库故障,让我注意到了 golang/sql 连接的超时问题。

设置连接超时的两种方式

1. SetMaxLifeTime && SetMaxIdleTime

这两个方法是官方标准库提供的,使用方式如下:

db, err := sql.Open("mysql", "root:@(127.0.0.1:3306)/oliver")
if err != nil {
    panic(err)
}
defer db.Close()
db.SetConnMaxLifetime(time.Second * 60) // 设置连接最大周期时间
db.SetConnMaxIdletime(time.Second * 30) // 设置连接最大空闲时间

在底层这两个方法的原理是一样的,通过方法 connectionCleaner 每秒对连接池 freeConn 中的连接进行超时判断,对已经超时的连接关闭操作。详见代码

需要注意的一点是,这里只对空闲连接检查,对于 InUse 的连接是无效的。所以像慢查询或者比较耗时的事务来说,这两个超时方法无能为力。接下来就请出第二种方法吧。

2. Context 超时设置

hhh, 感觉备受期待的方式竟然只是平平无奇路人皆知的 context。没错,先上代码。

db, err := sql.Open("mysql", "root:@(127.0.0.1:3306)/oliver")
if err != nil {
    panic(err)
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
go func() {
    time.Sleep(time.Second * 2)
    cancel()
}()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
    panic(err)
}
defer tx.Rollback()
// do something

如代码所示, 通过上述方法可以实现对慢查询以及耗时事务的超时控制,避免因连接长时间占用导致数据库锁,继而引发连接堆积。

写在最后

这两种方式其实缺一不可,一般我们感觉是设置了 SetConnMaxLifetime 就万无一失,其实还是存在连接池爆掉的风险。

我认为还应该遵从以下两点:

  1. 不要滥用事务。事务的职责应该清晰明了。
  2. 设置合理的超时时间以及上面没有提到的最大连接数。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions