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

調べたところ、無効な接続の使用によるものです.
基本シーンは次のとおりです.
  • クライアントはMySQLに接続し、SQLを実行した後、すぐに接続を閉じません.クライアントは接続プールに接続を保持します.
  • その後、MySQLサービスが再起動されたり、最大時間(wait_timeoutで定義され、一般的には8時間)以上接続されたりして、MySQLサービス側は接続を閉じました.
  • 次回クライアントでSQLを実行し、接続プールから無効な接続を取得すると、上記のエラーが報告されます.

  • 問題の検証
    検証コードの操作手順は次のとおりです.
  • まず、SQLを実行します.
  • 続いて、sleep 60 s.
  • その間、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.ソリューション
    シナリオ1アップグレードmysql drivergo-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
    

    つまり、無効な接続が発生すると自動的に再接続されます.
    関連するバグfixレコード、Bugfixesを参照:
    Mark connections as bad on error during ping (#875)
    Mark connections as bad on error during dial (#867)
    

    シナリオ2接続多重時間の設定go-mysql-driverを一時的にアップグレードできない場合は、SetConnMaxLifetime()で接続の多重化時間を設定できます.接続のデフォルトは永続的に多重化されます.
    接続の多重化時間は、接続がどのくらい使用されるかを示し、自動的に閉じます.
    接続が使用中の場合、接続の多重化時間を超えてもすぐに閉じるのではなく、接続が使用されなくなってから閉じることに注意してください.
    たとえば、接続多重化時間を60 sに設定します.
    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