1.問題
在使用go-sql-driver/mysql
連接MySQL 服務過程,隔一段時間,會報MySQL連接錯誤:
[mysql] 2020/05/09 02:02:01 packets.go:36: unexpected EOF
2020-05-09 02:02:01 ERROR goroutine 59835131 invalid connection
排查下來,是由於使用無效的連接導致的。
基本場景是:
- client 連接MySQL,執行SQL後,不立刻關閉連接。client保留連接在連接池中。
- 接着,MySQL服務發生重啓,
或者連接超過最大時長(由wait_timeout定義,一般是8小時), MySQL服務端關閉了連接。 - 下次 client 執行SQL,從連接池中拿到無效的連接,就會報以上錯誤。
問題驗證
驗證代碼操作步驟如下:
- 首先,執行SQL。
- 接着,sleep 60s。
- 這期間,將MySQL服務重啓一下。
- 程序 sleep 後,再次執行 SQL。
代碼如下:
import (
"database/sql"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
var DB *sql.DB
var 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 EOF
2020/05/24 12:01:49 exec sql failed: invalid connection
2020/05/24 12:02:49 success
從測試結果可以看到,當MySQL服務重啓後,原先的連接會失效,當再次被使用時,會報錯。
go-sql-driver/mysql 在這裏的實現上,並不會主動把問題連接從連接池中剔除,或者連接報錯後,自動重連。
2.解決方案
方案一 升級 mysql driver
將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: EOF
2020/05/24 15:12:33 success
2020/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,都會建立新的連接。
3.參考
packets.go:36: unexpected EOF (Invalid Connection)
MaxOpenConns, MaxIdleConns, ConnMaxLifetime的理解和調優
Configuring sql.DB for Better Performance