Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

about manager.Open #29

Closed
zplzpl opened this issue Nov 29, 2018 · 7 comments
Closed

about manager.Open #29

zplzpl opened this issue Nov 29, 2018 · 7 comments
Labels

Comments

@zplzpl
Copy link

zplzpl commented Nov 29, 2018

简单点就是Open(true)会创建一个脱缰的野马,没有 max life time...,默认交互式超时为:30S,没有意外你会遇到 invalid connection

建议
1、Open(false)
2、SetConnMaxLifetime
3、Ping()

@caibirdme
Copy link
Contributor

实际上Open只是返回了一个*sql.DB对象,而设置SetConnMaxLifetime每个项目都有自己的诉求,可以自己设置。这里Open(true)时ping一下只是简单帮忙确认一下连接是否可用。

@caibirdme
Copy link
Contributor

manager的职责是帮你拼接用于和mysql server进行握手的dataSourceName,本来需要手动设置的字面量这里改为了方法调用,仅此而已。对*sql.DB本身的行为设置还是交给用户

@zplzpl
Copy link
Author

zplzpl commented Dec 3, 2018

“Open(true)时ping一下只是简单帮忙确认一下连接是否可用”

Ping的时候,如果没有可用的connection,它是会创建的。那么这时候这条连接,在没设置max life time就是永久存在。

当进行执行query有可能会用到这条connection,而它已经被mysql server close掉之后,那么会抛出 invalid connection

@caibirdme
Copy link
Contributor

首先,根据官方文档

Open may just validate its arguments without creating a connection to the database. 
To verify that the data source name is valid, call Ping.

Open只是创建了一个内存中的结构体,只有Ping方法才会去检测你的连接设置是否正确(当然这意味着会新建一个连接)。这种检测是有必要的,因为数据库如果根本连不上的话,业务逻辑就完全没法处理,应该及早panic或者做其它处理。详见这个issue

其次,如果你新设置了MaxLifetime为d,d秒过后之前的连接就可能被清理掉。由于连接池也只会定期清掉一些空闲的过期的连接,换句话说,即使你设置了MaxLifetime,也不能保证所有连接都在有效期内,源码

		expiredSince := nowFunc().Add(-d)
		var closing []*driverConn
		for i := 0; i < len(db.freeConn); i++ {
			c := db.freeConn[i]
			if c.createdAt.Before(expiredSince) {
				closing = append(closing, c)
				last := len(db.freeConn) - 1
				db.freeConn[i] = db.freeConn[last]
				db.freeConn[last] = nil
				db.freeConn = db.freeConn[:last]
				i--
			}
		}
		db.maxLifetimeClosed += int64(len(closing))
		db.mu.Unlock()

		for _, c := range closing {
			c.Close()
		}

在高负载的情况下,使用完的连接通常不会放入freeConn队列中,而是直接交给下一个请求使用了。所以很可能你使用的连接早就过了maxLifetime了。

而连接本身被对端关闭或者信道出问题这种情况在任何时候都会出现,不会因为你设置了什么参数就可以避免的,如果有可能你需要自己去处理这些error

@zplzpl
Copy link
Author

zplzpl commented Dec 7, 2018

"连接本身被对端关闭或者信道出问题这种情况在任何时候都会出现"

那么遇到这种情况,你的处理方式会是?类似重试记录日志这样的操作?

其实我认为只要max life time 设置小于wait timeout 足够多(让其有机会放到freeConn),这里我也不好量化,但应该能避免很多问题。再报invalid connection,直接打印日志,就可以了。

@zplzpl
Copy link
Author

zplzpl commented Dec 7, 2018

被端关闭,原因有很多,但我发现实际场景更多是因为配置导致比如 wait timeout

@caibirdme
Copy link
Contributor

怎么设置MaxLifetime,怎么处理不可避免的连接错误,这就是用户方需要考虑的问题了,向上抛错误或者重试都是可能的,这不是gendry需要关注的。

一开始创建的那个连接在你设置完MaxLifetime之后下个周期就会被回收,它在这个期间出现问题的概率和你先设置完MaxLifetime再建立连接然后这个连接在下个回收周期到来之前出现问题的概率是几乎一样的。

不过如果你纠结的是Open(true)帮你Ping了一下的话,你完全可以Open(false)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants