在使用go-sql-driver/mysql
连接MySQL 服务过程,隔一段时间,会报MySQL连接错误:
[mysql] 2020/05/09 02:02:01 packets.go:36: unexpected EOF2020-05-09 02:02:01 ERROR goroutine 59835131 invalid connection
排查下来,是由于使用无效的连接导致的。
基本场景是:
验证代码操作步骤如下:
代码如下:
import ( "database/sql" "log" "time" _ "github.com/go-sql-driver/mysql")var DB *sql.DBvar dataBase = "root:Aa123456@tcp(192.168.1.101:3306)/?loc=Local&parseTime=true"func mysqlInit() { var err error DB, err = sql.Open("mysql", dataBase) if err != nil { log.Fatalln("open db fail:", err) } err = DB.Ping() if err != nil { log.Fatalln("ping db fail:", err) }}func main() { mysqlInit() for { execSql() time.Sleep(60*time.Second) }}func execSql() { _, err := DB.Exec("select 1") if err != nil { log.Println("exec sql failed:", err) return } log.Println("success")}
看下程序输出:
2020/05/24 12:00:49 success[mysql] 2020/05/24 12:01:49 packets.go:36: unexpected EOF2020/05/24 12:01:49 exec sql failed: invalid connection2020/05/24 12:02:49 success
从测试结果可以看到,当MySQL服务重启后,原先的连接会失效,当再次被使用时,会报错。
go-sql-driver/mysql 在这里的实现上,并不会主动把问题连接从连接池中剔除,或者连接报错后,自动重连。
将go-mysql-driver
升级到 Version 1.5 (2020-01-07)。
测试结果输出如下:
2020/05/24 15:11:33 success[mysql] 2020/05/24 15:12:33 packets.go:123: closing bad idle connection: EOF2020/05/24 15:12:33 success2020/05/24 15:13:33 success
也就是说,当遇到无效的连接时,会自动重新连接。
相关的bug fix 记录,见Bugfixes:
Mark connections as bad on error during ping (#875)Mark connections as bad on error during dial (#867)
如果暂时无法升级go-mysql-driver
,那么可以通过SetConnMaxLifetime()
设置连接复用时间,连接默认是永久复用的。
连接复用时间表示连接使用多长时间后,会自动关闭。
需要注意的是,如果连接正在使用中,即使超过连接复用时间,也不会立刻关闭,而是等到连接不再使用后,才会关闭。
例如,设置连接复用时间为60s。
db.SetConnMaxLifetime(60 * time.Second)
至于设置多长时间,需要根据自己的场景需要。
另外,有些地方建议使用SetMaxIdleConns()
设置idle 连接为0,这个是不推荐的。
这样的设置,会导致每次执行SQL,都会建立新的连接。
packets.go:36: unexpected EOF (Invalid Connection)
MaxOpenConns, MaxIdleConns, ConnMaxLifetime的理解和调优
Configuring sql.DB for Better Performance