0


深入理解mysql中的各种超时属性

1. 前言

connectTimeout

: 连接超时

loginTimeout

: 登录超时

socketTimeout

: Socket网络超时,即读超时

queryTimeout

: sql执行超时

transactionTimeout

:spring事务超时

innodb_lock_wait_timeout

:innodb锁等待超时

wait_timeout

:非交互式连接关闭前的等待时间

interactive_wait_timeout

:交互式连接关闭前的等待时间

netTimeoutForStreamingResults

:mysql server网络回包写超时(针对大量数据查询的sql)

2 connectTimeout和loginTimeout

mysql数据库在建立连接时,会在connectTimeout 、loginTimeout这两个变量中的取其之一作为真正的连接超时属性,具体取值逻辑是在

com.mysql.cj.protocol.StandardSocketFactory#connect

建立连接时调用的

getRealTimeout

方法。

在这里插入图片描述
在这里插入图片描述
getRealTimeout方法的expectedTimeout参数值是connnectTimeout.
getRealTimeout的逻辑是如果loginTimeout有值(this.loginTimeoutCountdown > 0)且[connnectTimeout没值(this.loginTimeoutCountdown > 0)或connnectTimeout值大于loginTimeout]则取值loginTimeout,否则取值connnectTimeout。也就是说这个方法取值思路是:两者都有值时,在两者中取较小的那个值作为最终的连接超时时间,两者中只有一个有值时,取有值那个参数作为最终的连接超时时间。
既然说到这儿了,那么我们应该搞清楚

connnectTimeout
loginTimeout

这两个参数的来源是在哪儿?

1) loginTimeout

loginTimeout参数来源于驱动管理器的

loginTimeout

,在

com.mysql.cj.jdbc.ConnectionImpl#connectOneTryOnly

方法中可以看到这个取值逻辑。

在这里插入图片描述

貌似我们没有给驱动管理器设置过登录超时这参数,DriverManager#loginTimeout的默认值是0,不应该是30。
其实这DriverManager#loginTimeout现在的值是HikariCP连接池给我们设的默认值。HikariPool构造方法中初始化执行

PoolBase#initializeDataSource

时调用

setLoginTimeout

去给DriverManager设置登录超时
在这里插入图片描述
在这里插入图片描述
上面

PoolBase#setLoginTimeout(DataSource)

方法中的

dataSource

参数是

com.zaxxer.hikari.util.DriverDataSource

类的实例,而

com.zaxxer.hikari.util.DriverDataSource#setLoginTimeout(int)

方法就是会直接给DriverManager的

loginTimeout

设值。

//com.zaxxer.hikari.util.DriverDataSource@OverridepublicvoidsetLoginTimeout(int seconds)throwsSQLException{DriverManager.setLoginTimeout(seconds);}

从下面的代码可以看出,

PoolBase#connectionTimeout

属性值来源于

HikariConfig#connectionTimeout

,而

HikariConfig#connectionTimeout

的属性值又来源于配置文件中的

spring.datasource.hikari.connection-timeout

属性值,若配置文件中的此属性值为空,则取默认值30秒

PoolBase(finalHikariConfig config){this.config = config;//....//PoolBase#connectionTimeout来自HikariConfig#connectionTimeoutthis.connectionTimeout = config.getConnectionTimeout();this.validationTimeout = config.getValidationTimeout();this.lastConnectionFailure =newAtomicReference<>();//....initializeDataSource();}
publicclassHikariConfigimplementsHikariConfigMXBean{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(HikariConfig.class);privatestaticfinalchar[]ID_CHARACTERS="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();privatestaticfinallongCONNECTION_TIMEOUT=SECONDS.toMillis(30);privatestaticfinallongVALIDATION_TIMEOUT=SECONDS.toMillis(5);privatestaticfinallongIDLE_TIMEOUT=MINUTES.toMillis(10);privatestaticfinallongMAX_LIFETIME=MINUTES.toMillis(30);/**
    * Default constructor
    */publicHikariConfig(){
      dataSourceProperties =newProperties();
      healthCheckProperties =newProperties();

      minIdle =-1;
      maxPoolSize =-1;
      maxLifetime =MAX_LIFETIME;//默认值30秒
      connectionTimeout =CONNECTION_TIMEOUT;
      validationTimeout =VALIDATION_TIMEOUT;
      idleTimeout =IDLE_TIMEOUT;
      initializationFailTimeout =1;
      isAutoCommit =true;String systemProp =System.getProperty("hikaricp.configurationFile");if(systemProp !=null){loadProperties(systemProp);}}}

在这里插入图片描述

2) connectTimeout

connectTimeout

参数因为是在

com.mysql.cj.conf.PropertyKey
com.mysql.cj.conf.PropertyDefinitions

类中定义的,它的默认值是0,即表示可以无限时长地连接等待;所以它需要在配置文件jdbc连接属性

spring.datasource.url

上设值,如

jdbc:mysql://localhost:3306/{xx_db}?connectTimeout={numTime}

在这里插入图片描述
在这里插入图片描述

3. socketTimeout

socketTimeout是socket超时时间,即读超时。它在

com.mysql.cj.conf.PropertyKey

com.mysql.cj.conf.PropertyDefinitions

类中定义,默认值是0,它也是在jdbc连接url上配置。
在这里插入图片描述

在这里插入图片描述
socketTimeout参数在

com.mysql.cj.protocol.a.NativeSocketConnection#connect

方法中真正得以应用,本质上就是为Socket为SO_TIMEOUT选项设值,tcp/ip协议底层对SO_TIMEOUT提供了支持,这跟应用层mysql协议无关。而

queryTimeout
netTimeoutForStreamingResults

参数都是应用层mysql协议对它的支撑。
在这里插入图片描述
在这里插入图片描述

4 queryTimeout

queryTimeout

: sql执行超时。jdbc规范的Statement定义了这个超时时间(见java.sql.Statement#setQueryTimeout接口方法)。
如果使用原生的jdbc,则需要手动调用

ava.sql.Statement#setQueryTimeout

设置sql执行超时。
国内实际上一般都使用mybatis这个orm框架,我们可以在配置文件中用

mybatis.configuration.default-statement-timeout

配置全局默认的queryTimeout,当然也可以在指定的Mapper方法中单独配置queryTimeout(优先级比mybatis.configuration.default-statement-timeout高) 。
在这里插入图片描述
mybatis框架

BaseStatementHandler#prepare

中调用setStatementTimeout设值sql超时时间。
其逻辑是先取当前指定Statement的queryTimeout,若没有则取全局默认的queryTimemout。然后把此值跟spring事务注解

@Transactional

配置的事务超时时间进行比较,最终的queryTimeout取两者中较小的那个值。
在这里插入图片描述

//StatementUtilpublicstaticvoidapplyTransactionTimeout(Statement statement,Integer queryTimeout,Integer transactionTimeout)throwsSQLException{if(transactionTimeout ==null){return;}if(queryTimeout ==null|| queryTimeout ==0|| transactionTimeout < queryTimeout){
     statement.setQueryTimeout(transactionTimeout);}}

接下来来看看queryTimeout的实现原理,

ClientPreparedStatement#executeInternal

方法在执行sql之前会调用

startQueryTimer

尝试获取一个

CancelQueryTask

超时任务 ,在执行完sql后尝试取消这个超时任务的,如果在超时前完成了sql查询,这时任务就被成功取消了,超时任务不会被执行。在这里插入图片描述

startQueryTimer方法中的timeout参数是sql执行超时时间,

PropertyKey.enableQueryTimeouts

属性默认值是true。因此只要sql执行超时不为空,就会创建一个CancelQueryTaskImpl任务,并且这个任务会在到达sql执行超时的时间线被执行(session.getCancelTimer().schedule(timeoutTask, timeout)延迟调度任务)。在这里插入图片描述
我们再往下看看这个

CancelQueryTaskImpl

任务是如何运行的。从下面的代码可以看出,

CancelQueryTaskImpl.run

方法首先启动了一个线程,然后在这个线程中执行sql脚本

KILL QUERY {query_threadId}

去杀掉这个查询线程。注意: 这里是每次sql执行都会启动一个新线程,没有使用线程池(应该是为了保证超时任务能得到及时的调度,线程池中的线程数是有限的,任务数过多就会放在任务队列中,任务调度不可避免地有一定延迟),在高并发的情况下会创建大量的线程,可能导致系统资源占用过高,甚至导致jvm虚拟机崩溃退出,所以在高并发环境中不建议使用sql执行超时这个功能。

在这里插入图片描述

5. transactionTimeout 和 innodb_lock_wait_timeout

transactionTimeout

:spring事务注解

@Transactional

的超时时间,上面说到了,这个值将会作为sql执行超时,可以说它是客户端的事务超时参数,mysql本身是不支持事务超时的,mysql只有请求锁超时概念,这个是spring框架实现的事务超时。

innodb_lock_wait_timeout

: mysql server的环境变量,用于设置事务在等待获取锁时的超时时间。当一个事务请求锁资源时,如果该资源已经被其他事务锁定,那么该事务就会进入等待状态。如果一个事务等待获取锁的时间超过了该设置的时间,MySQL 将会自动中断该事务。

6. wait_timeout 和interactive_wait_timeout

wait_timeout

: 数据库服务端非交互式连接关闭前的等待时间。非交互链接是指JDBC等编程工具建立的数据库连接。

interactive_wait_timeout

: 数据库服务端交互式连接关闭前的等待时间。交互式连接是指各种mysql UI客户端建立的连接。
这两个参数都是mysql server的环境变量,可以通过sql脚本

set [GLOBAL] VARIABLES wait_timeout={timeNum};

设置。
mysql的默认全局wait_timeout是86400秒,大致8小时。这个参数过大,可能导致mysql服务端一直有Sleep的空闲线程,连接得不到释放。当然过它过小也会导致在执行sql脚本时数据库连接被莫名的关闭,发生’MySQL server has gone away’这种异常。
HikariCP连接池有一个

maxLifetime

,这个参数表示一个连接的最大存活时间,达到这个阈值就JDBC客户端就主动关闭这个连接。这里就避免了mysql客户端

wait_timeout

有大量的空闲线程.

7. netTimeoutForStreamingResults

netTimeoutForStreamingResults

:主要用来在处理流式结果集时mysql server返回大量数据的超时时间,防止等待结果集的时间过长。
在这里插入图片描述
setupStreamingTimeout 根据流结果超时时间(PropertyKey.netTimeoutForStreamingResults的默认值是600)和是否需要流结果集方法

createStreamingResultSet

来综合判断是否需要向服务端发送

net_write_timeout

属性。
在这里插入图片描述

protectedbooleancreateStreamingResultSet(){return((this.query.getResultType()==Type.FORWARD_ONLY)&&(this.resultSetConcurrency ==java.sql.ResultSet.CONCUR_READ_ONLY)//getResultFetchSize默认值是0&&(this.query.getResultFetchSize()==Integer.MIN_VALUE));}

本文转载自: https://blog.csdn.net/Xiaowu_First/article/details/139403191
版权归原作者 蜀中孤鹰 所有, 如有侵权,请联系我们删除。

“深入理解mysql中的各种超时属性”的评论:

还没有评论