MySQL 的連接時長控制--interactive_timeout和wait_timeout

在用MySQL客戶端對數據庫進行操作時,如果一段時間沒有操作,再次操作時,常常會報如下錯誤:

ERROR 2013 (HY000): Lost connection to MySQL server during query
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...

這個報錯信息就意味着當前的連接已經斷開,需要重新建立連接。

那麼,連接建立後,連接的時長是如何確定的呢?

在MySQL中,這個與兩個參數interactive_timeoutwait_timeout的設置有關。

注:以下說明基於MySQL 5.7.

1.interactive_timeout和wait_timeout的定義

首先,看看官方文檔對於這兩個參數的定義。

interactive_timeout

The number of seconds the server waits for activity on an interactive connection before closing it. An interactive client is defined as a client that uses the CLIENT_INTERACTIVE option to mysql_real_connect(). See also wait_timeout.

interactive_timeout參數,定義了對於交互式連接,服務端等待數據的最大時間。如果超過這個時間,服務端仍然沒有收到數據,則會關閉連接。

所謂交互式client,是指調用mysql_real_connect()函數建立連接時,設置了CLIENT_INTERACTIVE選項。比較常用的就是命令行終端。

查看interactive_timeout的值:

mysql> show global variables like  'interactive_timeout%';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
+---------------------+-------+
1 row in set (0.01 sec)

默認是28800,單位秒,即8個小時

wait_timeout

The number of seconds the server waits for activity on a noninteractive connection before closing it.

On thread startup, the session wait_timeout value is initialized from the global wait_timeout value or from the global interactive_timeout value, depending on the type of client (as defined by the CLIENT_INTERACTIVE connect option to mysql_real_connect()). See also interactive_timeout.

wait_timeout參數,定義對於非交互式連接,服務端等待數據的最長時間。如果超過這個時間,服務端仍然沒有收到數據,則會關閉連接。

在連接線程啓動的時候,根據連接的類型,決定會話級的wait_timeout的值是初始化爲全局的wait_timeout,還是全局的interactive_timeout。即如果是交互式連接,會話變量wait_timeout初始化爲全局的interactive_timeout,否則,初始化爲全局的wait_timeout。

查看wait_timeout的值:

mysql> show global variables like  'wait_timeout%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

默認同樣是28800s,即8小時。

根據上述定義,兩者的區別顯而易見:interactive_timeout針對交互式連接,wait_timeout針對非交互式連接。所謂的交互式連接,即在mysql_real_connect()函數中使用了CLIENT_INTERACTIVE選項。

 說得直白一點,通過mysql命令行終端連接數據庫是交互式連接,通過jdbc等連接數據庫是非交互式連接。 

下面來測試一下,確認如下問題:

  • 控制連接最大空閒時長的是哪個參數。
  • 會話變量wait_timeout的繼承問題

2.控制連接最大空閒時長的是哪個參數

先給出答案:wait_timeout

接下來進行驗證。

2.1 只修改會話的wait_timeout參數

查看當前會話的wait_timeout和interactive_timeout。

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

設置當前會話的wait_timeout爲10s

mysql> set session wait_timeout=10;
Query OK, 0 rows affected (0.00 sec)

再次查看

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 10    |
+---------------------+-------+
2 rows in set (0.00 sec)

等待10s,再次查看

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    8
Current database: *** NONE ***

+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.01 sec)

可以看到,等待10s後再執行操作,原來的連接已經斷開,並重新建立連接。

2.2 只修改會話的interactive_timeout參數

首先查看當前會話的interactive_timeout和wait_timeout.

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

接着,設置當前會話的interactive_timeout爲10s

mysql> set session interactive_timeout=10;
Query OK, 0 rows affected (0.00 sec)

再次查看

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 10    |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.01 sec)

等待10s,再次查看

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 10    |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

可以看到,即使等待10後,連接是正常的。所以,設置interactive_timeout,對連接的時長沒有影響。

3.會話變量wait_timeout的繼承問題

上面已經提到,如果是交互式連接,則繼承自全局變量interactive_timeout的值,如果是非交互式連接,則繼承自全局變量wait_timeout的值。

下面進行驗證。

3.1 只修改全局變量interactive_timeout的值

首先查看全局的interactive_timeout和wait_timeout。

mysql> show global  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

接着,設置全局的interactive_timeout爲10s。

mysql> set global INTERACTIVE_TIMEOUT=10;
Query OK, 0 rows affected (0.00 sec)

再次查看

mysql> show global  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 10    |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

開啓另外一個MySQL客戶端,查看會話變量的值:

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 10    |
| wait_timeout        | 10    |
+---------------------+-------+
2 rows in set (0.01 sec)

發現,WAIT_TIMEOUT的值已經變爲10了。

等待10s後,再次查看,會發現原來的連接已經斷開,連接的時長設置已經生效。

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    70
Current database: *** NONE ***

+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 10    |
| wait_timeout        | 10    |
+---------------------+-------+
2 rows in set (0.01 sec)

但通過非終端測試,wait_timeout的值依舊是28800:

package main

import (
        "database/sql"
        "log"

        _ "github.com/go-sql-driver/mysql"
)

var DB *sql.DB
var dataBase = "root:Aa123456@tcp(127.0.0.1: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)
        }

        DB.SetMaxOpenConns(1)

        err = DB.Ping()
        if err != nil {
                log.Fatalln("ping db fail:", err)
        }
}

func main() {
        mysqlInit()
        execSql()
}

func execSql() {
        var variableName string
        var value int
        sql := "show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout')"
        rows, err := DB.Query(sql)
        if err != nil {
                log.Println("query failed:", err)
                return
        }

        defer rows.Close()

        for rows.Next() {
                err = rows.Scan(&variableName, &value)
                if err != nil {
                        log.Println("rows.Scan failed:", err)
                        return
                }

                log.Println("variable_name:", variableName, ", value:", value)
        }
}

output:

2019/10/13 17:11:22 variable_name: interactive_timeout , value: 10
2019/10/13 17:11:22 variable_name: wait_timeout , value: 28800

結果輸出如下

INTERACTIVE_TIMEOUT:  10
WAIT_TIMEOUT:  28800

3.2 只修改全局變量wait_timeout的值

首先查看全局的interactive_timeout和wait_timeout。

mysql> show global  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

接着,將全局的WAIT_TIMEOUT設置爲20s。

mysql> set global WAIT_TIMEOUT=20;
Query OK, 0 rows affected (0.07 sec)

再次查看

mysql> show global  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 20    |
+---------------------+-------+
2 rows in set (0.00 sec)

開啓另外一個mysql客戶端,查看會話變量的值

mysql> show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout');
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
| wait_timeout        | 28800 |
+---------------------+-------+
2 rows in set (0.00 sec)

WAIT_TIMEOUT的值依舊是28800.

查看非終端的代碼執行的結果:

func execSql() {
        var variableName string
        var value int
        sql := "show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout')"
        rows, err := DB.Query(sql)
        if err != nil {
                log.Println("query failed:", err)
                return
        }

        defer rows.Close()

        for rows.Next() {
                err = rows.Scan(&variableName, &value)
                if err != nil {
                        log.Println("rows.Scan failed:", err)
                        return
                }

                log.Println("variable_name:", variableName, ", value:", value)
        }
}

output:

2019/10/13 17:23:10 variable_name: interactive_timeout , value: 28800
2019/10/13 17:23:10 variable_name: wait_timeout , value: 20

修改程序,執行sql語句後,等待25s後,再次執行sql語句,查看執行情況。

func main() {
        mysqlInit()
        for {
                execSql()
                time.Sleep(25*time.Second)
        }
}

func execSql() {
        var variableName string
        var value int
        sql := "show session  variables where Variable_name in ('interactive_timeout', 'wait_timeout')"
        rows, err := DB.Query(sql)
        if err != nil {
                log.Println("query failed:", err)
                return
        }

        defer rows.Close()

        for rows.Next() {
                err = rows.Scan(&variableName, &value)
                if err != nil {
                        log.Println("rows.Scan failed:", err)
                        return
                }

                log.Println("variable_name:", variableName, ", value:", value)
        }
}

output:

2019/10/13 17:26:46 variable_name: interactive_timeout , value: 28800
2019/10/13 17:26:46 variable_name: wait_timeout , value: 20
[mysql] 2019/10/13 17:27:11 packets.go:36: unexpected EOF
[mysql] 2019/10/13 17:27:11 packets.go:141: write tcp 127.0.0.1:53878->127.0.0.1:3306: write: broken pipe
2019/10/13 17:27:11 variable_name: interactive_timeout , value: 28800
2019/10/13 17:27:11 variable_name: wait_timeout , value: 20

可以看到,等待25s後,再次執行sql,此時連接已經斷開。
底層又重新建立連接。

4.總結

  • 控制連接最大空閒時長的wait_timeout參數。

  • 關於wait_timeout的繼承

    • 對於非交互式連接,類似於jdbc連接,wait_timeout的值繼承自全局變量wait_timeout。
    • 對於交互式連接,類似於mysql命令行終端,wait_timeout的值繼承全局變量interactive_timeout。
  • 判斷一個連接的空閒時間,可通過show processlist輸出中Sleep狀態的時間

mysql> show processlist;
+----+------+----------------------+------+---------+------+-------+------------------+
| Id | User | Host                 | db   | Command | Time | State | Info             |
+----+------+----------------------+------+---------+------+-------+------------------+
|  2 | root | localhost            | NULL | Query   |    0 | init  | show processlist |
|  6 | repl | 192.132.2.66:56001   | NULL | Sleep   | 1201 |       | NULL             |
+----+------+----------------------+------+---------+------+-------+------------------+
2 rows in set (0.03 sec)

5.參考

https://www.cnblogs.com/ivictor/p/5979731.html

http://www.cnblogs.com/cenalulu/archive/2012/06/20/2554863.html

http://www.cnblogs.com/Alight/p/4118515.html

http://ronaldbradford.com/blog/sqlstatehy000-general-error-2006-mysql-server-has-gone-away-2013-01-02/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章