MySQL:参数wait_timeout和interactive_timeout以及空闲超时的实现

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 水平有限,如果有误请指出源码版本:percona 5.7.22 一、参数意思 这里简单解释一下两个参数含义如下: interactive_timeout:The number of seconds the server waits for activity on an interactive .

水平有限,如果有误请指出
源码版本:percona 5.7.22


一、参数意思

这里简单解释一下两个参数含义如下:

  • interactive_timeout:The number of seconds the server waits for activity on an interactive connection before closing
  1. An interactive client is defined as a client that uses the CLIENT_INTERACTIVE option to mysql_real_connect()
  • 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())

他们都是session/global级别的,简单的说前者用于描述交互式的客户端的空闲超时,后者用于非交互式的客户端的空闲超时,但是这里也揭示了,如果是交互式客户端连接的session那么wait_timeout将被interactive_timeout覆盖掉,换句话说如果是非交互式的客户端连接的session将不会使用interactive_timeout覆盖掉wait_timeout,也就是interactive_timeout没有任何作用了。

二、参数内部表示

  • interactive_timeout:
static Sys_var_ulong Sys_interactive_timeout( vio_io_wait
       "interactive_timeout",
       "The number of seconds the server waits for activity on an interactive "
       "connection before closing it",
       SESSION_VAR(net_interactive_timeout),
       CMD_LINE(REQUIRED_ARG),
       VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1));
  • wait_timeout:
static Sys_var_ulong Sys_net_wait_timeout(
       "wait_timeout",
       "The number of seconds the server waits for activity on a "
       "connection before closing it",
       SESSION_VAR(net_wait_timeout), CMD_LINE(REQUIRED_ARG),
       VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
       DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1));

我们可以看到内部而言参数interactive_timeout表示为net_interactive_timeout,wait_timeout表示为net_wait_timeout。

三、interactive_timeout覆盖wait_timeout

实际上这个操作只会在用户登陆的时候才出现函数对应server_mpvio_update_thd,如下:

server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio) do_command
{
  thd->max_client_packet_length= mpvio->max_client_packet_length;
  if (mpvio->protocol->has_client_capability(CLIENT_INTERACTIVE)) //这里做判断
    thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;//这里覆盖

这里我们可以明确看到有覆盖操作,并且我们也能看到这里的if条件是如果是CLIENT_INTERACTIVE类型的客户端连接才会做覆盖。

栈帧如下:

#0  server_mpvio_update_thd (thd=0x7ffe7c012940, mpvio=0x7fffec0f6140) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/auth/sql_authentication.cc:2014
#1  0x0000000000f01787 in acl_authenticate (thd=0x7ffe7c012940, command=COM_CONNECT, extra_port_connection=false)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/auth/sql_authentication.cc:2246
#2  0x0000000001571149 in check_connection (thd=0x7ffe7c012940, extra_port_connection=false)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_connect.cc:1295
#3  0x00000000015712dc in login_connection (thd=0x7ffe7c012940, extra_port_connection=false)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_connect.cc:1352
#4  0x0000000001571bfe in thd_prepare_connection (thd=0x7ffe7c012940, extra_port_connection=false)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_connect.cc:1516
#5  0x000000000170e642 in handle_connection (arg=0x6781c30) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/conn_handler/connection_handler_per_thread.cc:306

那么我们这里可以得到一个结论,只在登陆的时候会判断连接是否是交互式的,如果是则覆盖掉参数wait_timeout,但是一旦连接后将不会发生覆盖操作,即便我们再次修改interactive_timeout的值也不会覆盖,后面我们会看到实际上生效的参数只有wait_timeout。

四、超时的实现

实际上每次执行任何一个命令都会做一次wait_timeout值的重新检查和赋值给网络read_timeout值。在函数do_command中我们可以发现my_net_set_read_timeout(net, thd->get_wait_timeout());步骤,这个步骤就是将我们的wait_timeout赋值给网络read_timeout值,其中包含片段

  if (net->read_timeout == timeout) //如果read_timeout和wait_timeout相等
    DBUG_VOID_RETURN;//不需要做操作直接return
  net->read_timeout= timeout;//否则进行赋值。
  if (net->vio)
    vio_timeout(net->vio, 0, timeout);//这里会进行net->vio.read_timeout的赋值

执行完这个步骤后wait_timeout就生效了,然后就会执行命令,执行完命令后,整个线程会再次回到do_command函数,再做一次my_net_set_read_timeout函数生效其中的wait_timeout参数,中并且堵塞接受命令(后面可以看到是poll实现的),这个时候wait_timeout就起作用了。整个栈帧如下:

#0  vio_io_wait (vio=0x7ffe7c015520, event=VIO_IO_EVENT_READ, timeout=10000) at /root/mysqlall/percona-server-locks-detail-5.7.22/vio/viosocket.c:1119
#1  0x0000000001e4d5f6 in vio_socket_io_wait (vio=0x7ffe7c015520, event=VIO_IO_EVENT_READ) at /root/mysqlall/percona-server-locks-detail-5.7.22/vio/viosocket.c:116
#2  0x0000000001e4d6d2 in vio_read (vio=0x7ffe7c015520, buf=0x7ffe7c061c10 "\001", size=4) at /root/mysqlall/percona-server-locks-detail-5.7.22/vio/viosocket.c:171
#3  0x00000000014c6ceb in net_read_raw_loop (net=0x7ffe7c028440, count=4) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/net_serv.cc:672
#4  0x00000000014c6ec2 in net_read_packet_header (net=0x7ffe7c028440) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/net_serv.cc:756
#5  0x00000000014c6fcb in net_read_packet (net=0x7ffe7c028440, complen=0x7fffec0c5c58) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/net_serv.cc:822
#6  0x00000000014c715e in my_net_read (net=0x7ffe7c028440) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/net_serv.cc:899
#7  0x00000000014de010 in Protocol_classic::read_packet (this=0x7ffe7c027bf8) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/protocol_classic.cc:808
#8  0x00000000014de514 in Protocol_classic::get_command (this=0x7ffe7c027bf8, com_data=0x7fffec0c5d70, cmd=0x7fffec0c5d98)
    at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/protocol_classic.cc:965
#9  0x00000000015c5699 in do_command (thd=0x7ffe7c0268e0) at /root/mysqlall/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:960

最终会调入vio_io_wait函数,如下是其中的部分片段,我们可以清楚看到实际上所谓的空闲超时实际上就是我们的pool实现的。

switch ((ret= poll(&pfd, 1, timeout)))  
  {
  case -1:
    /* On error, -1 is returned. */
    break;
  case 0:
    /*
      Set errno to indicate a timeout error.
      (This is not compiled in on WIN32.)
    */
    errno= SOCKET_ETIMEDOUT;
    break;
  default:
    /* Ensure that the requested I/O event has completed. */
    DBUG_ASSERT(pfd.revents & revents);
    break;
  }

因此整个步骤就是

  • loop
  • 做wait_timeout参数检查并且赋值。
  • 堵塞接受命令由poll函数实现,通过poll函数的超时参数也实现了空闲等待超时。(如果不发送命令就堵塞在这里)
  • 命令来到退出堵塞。
  • 再次做wait_timeout参数检查并且赋值。
  • 执行命令。
  • goto loop

五、测试

我这里就用mysql客户端和pymysql进行交互和非交互连接的测试。

  • 交互式mysql客户端会话interactive_timeout 参数覆盖wait_timeout参数
mysql> show variables like 'wait_timeout%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.02 sec)


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

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

mysql> exit
Bye
[root@gp1 log]# /mysqldata/mysql3340/bin/mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.7.22-22-debug-log Source distribution

Copyright (c) 2009-2018 Percona LLC and/or its affiliates
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

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

mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 20    |
+---------------+-------+
1 row in set (0.02 sec)
  • 交互式mysql客户端会话登陆期间修改interactive_timeout不生效,更改wait_timeout生效。
mysql> show variables like 'interactive_timeout';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800  |
+---------------------+-------+
1 row in set (0.02 sec)

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

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

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

mysql> show variables like 'interactive_timeout';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 5     |
+---------------------+-------+
1 row in set (0.02 sec)

等待5秒,并未生效

mysql> select sysdate();
+---------------------+
| sysdate()           |
+---------------------+
| 2019-02-28 17:24:29 |
+---------------------+
1 row in set (0.00 sec)

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

等待5秒 发现断开了
mysql> show variables like 'wait_timeout';
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    10
  • 使用python连接非交互式客户端interactive_timeout 参数不会覆盖wait_timeout参数

我们可以简单的写一个python脚本如下:

import socket
import pymysql.cursors
import psutil
import subprocess


mysql_con = {"host":"192.168.99.95","port":3340,"user":"pycon","passwd":"gelc123","db":"test"}

def main():
    sqlwait = "show variables like 'wait_timeout'"
    sqlinter = "show variables like 'interactive_timeout'"
    sql_c_inter = "set global interactive_timeout=10"

    connect = pymysql.Connect(host=mysql_con["host"], port=mysql_con["port"], user=mysql_con["user"],
                              passwd=mysql_con["passwd"], db=mysql_con["db"])
    cursor = connect.cursor()
    ##查看初始值
    cursor.execute(sqlwait)
    ret_wait = cursor.fetchone()
    cursor.execute(sqlinter)
    ret_inter = cursor.fetchone()
    print("before change: {}".format(ret_wait+ret_inter))

    ##更改值
    cursor.execute(sql_c_inter)
    connect.close()##关闭连接

    ##重新登陆开启连接
    connect = pymysql.Connect(host=mysql_con["host"], port=mysql_con["port"], user=mysql_con["user"],
                              passwd=mysql_con["passwd"], db=mysql_con["db"])
    cursor = connect.cursor()

    cursor.execute(sqlwait)
    ret_wait = cursor.fetchone()
    cursor.execute(sqlinter)
    ret_inter = cursor.fetchone()

    print("after change: {}".format(ret_wait+ret_inter))

    ##恢复值
    sql_c_inter = "set global interactive_timeout=28800"
    cursor.execute(sql_c_inter)

    connect.close()#关闭连接

##程序开始
if __name__ == '__main__':
    main()

得到的测试结果如下:

before change: ('wait_timeout', '28800', 'interactive_timeout', '28800')
after change: ('wait_timeout', '28800', 'interactive_timeout', '10')

如果是交互是客户端会话的话wait_timeout也应该是10。

六、总结

  • 内部来讲只有wait_timeout参数会传递到网络层设置,而interactive_timeout参数只会在会话登陆的时候判断是否是交互式客户端会话如果是则进行wait_timeout=interactive_timeout的覆盖,如果不是则不生效的。
  • 一旦会话登陆成功如果想要会话级别修改超时参数,不管交互式还是非交互式都是修改wait_timeout(set wait_timeout)参数才会生效。
  • 内部实现空闲超时是通过poll函数的超时参数实现的。
  • 简单来说interactive_timeout对交互式客户端连接生效,wait_timeout对非交互式客户端连接生效。
  • 对于参数生效的过程如下:
  1. loop
  2. 做wait_timeout参数检查并且赋值。
  3. 堵塞接受命令由poll函数实现,通过poll函数的超时参数也实现了空闲等待超时。(如果不发送命令就堵塞在这里)
  4. 命令来到退出堵塞。
  5. 再次做wait_timeout参数检查并且赋值。
  6. 执行命令。
  7. goto loop
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
18天前
|
缓存 监控 关系型数据库
如何根据监控结果调整 MySQL 数据库的参数以提高性能?
【10月更文挑战第28天】根据MySQL数据库的监控结果来调整参数以提高性能,需要综合考虑多个方面的因素
57 1
|
2月前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
547 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
1月前
|
SQL 关系型数据库 MySQL
数据库:MYSQL参数max_allowed_packet 介绍
数据库:MYSQL参数max_allowed_packet 介绍
71 2
|
6月前
|
NoSQL 关系型数据库 MySQL
实时计算 Flink版操作报错之同步MySQL分库分表500张表报连接超时,是什么原因
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
6月前
|
存储 监控 关系型数据库
MySQL 参数innodb_read_io_threads
`innodb_read_io_threads` 是 MySQL 数据库中 InnoDB 存储引擎的一个配置参数,它用于指定后台线程池中用于处理读取 I/O 请求的线程数量。InnoDB 存储引擎负责管理数据库的物理存储和检索,是 MySQL 最常用的存储引擎之一。 ### 参数说明 - **名称**: `innodb_read_io_threads` - **默认值**: 4 - **范围**: 1 到 64 - **动态修改**: 不能动态修改(需要重启服务器) - **适用版本**: MySQL 5.6 及以上版本 ### 作用 `innodb_read_io_threads`
597 1
|
6月前
|
缓存 关系型数据库 MySQL
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
286 0
|
4月前
|
关系型数据库 MySQL 分布式数据库
PolarDB产品使用问题之使用polardb for mysql数据库的外网地址在程序中连接经常超时,如何解决
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
6月前
|
SQL 关系型数据库 MySQL
【Mysql】MYSQL参数max_allowed_packet 介绍
【Mysql】MYSQL参数max_allowed_packet 介绍
609 0
|
4月前
|
分布式计算 关系型数据库 MySQL
MySQL超时参数优化与DataX高效数据同步实践
通过合理设置MySQL的超时参数,可以有效地提升数据库的稳定性和性能。而DataX作为一种高效的数据同步工具,可以帮助企业轻松实现不同数据源之间的数据迁移。无论是优化MySQL参数还是使用DataX进行数据同步,都需要根据具体的应用场景来进行细致的配置和测试,以达到最佳效果。
|
5月前
|
SQL 缓存 关系型数据库