• golang mysql unexpected EOF(invalid connection)


    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,从连接池中拿到无效的连接,就会报以上错误。

    问题验证

    验证代码操作步骤如下:

    1. 首先,执行SQL。
    2. 接着,sleep 60s。
    3. 这期间,将MySQL服务重启一下。
    4. 程序 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

    go-sql-driver changelog

  • 相关阅读:
    Django-01
    tkinter模块常用参数(python3)
    Python3 数据可视化之matplotlib、Pygal、requests
    python面试题(二)
    python 面试题(一)
    Python 用Redis简单实现分布式爬虫
    用python的正则表达式实现简单的计算器功能
    Python操作 RabbitMQ、Redis、Memcache
    自定义线程池
    21天学通Python课后实验题4.6
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/12951188.html
Copyright © 2020-2023  润新知