深入明确mysql中的各种超时属性

打印 上一主题 下一主题

主题 825|帖子 825|积分 2475

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设值。
  1. //com.zaxxer.hikari.util.DriverDataSource
  2.   @Override
  3.    public void setLoginTimeout(int seconds) throws SQLException
  4.    {
  5.       DriverManager.setLoginTimeout(seconds);
  6.    }
复制代码
从下面的代码可以看出,PoolBase#connectionTimeout属性值泉源于HikariConfig#connectionTimeout,而HikariConfig#connectionTimeout的属性值又泉源于配置文件中的spring.datasource.hikari.connection-timeout属性值,若配置文件中的此属性值为空,则取默认值30秒
  1.   PoolBase(final HikariConfig config)
  2.    {
  3.       this.config = config;
  4.       //....
  5.       //PoolBase#connectionTimeout来自HikariConfig#connectionTimeout
  6.       this.connectionTimeout = config.getConnectionTimeout();
  7.       this.validationTimeout = config.getValidationTimeout();
  8.       this.lastConnectionFailure = new AtomicReference<>();
  9.         //....
  10.       initializeDataSource();
  11.    }
复制代码
  1. public class HikariConfig implements HikariConfigMXBean
  2. {
  3.    private static final Logger LOGGER = LoggerFactory.getLogger(HikariConfig.class);
  4.    private static final char[] ID_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
  5.    private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
  6.    private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
  7.    private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
  8.    private static final long MAX_LIFETIME = MINUTES.toMillis(30);
  9.    /**
  10.     * Default constructor
  11.     */
  12.    public HikariConfig()
  13.    {
  14.       dataSourceProperties = new Properties();
  15.       healthCheckProperties = new Properties();
  16.       minIdle = -1;
  17.       maxPoolSize = -1;
  18.       maxLifetime = MAX_LIFETIME;
  19.       //默认值30秒
  20.       connectionTimeout = CONNECTION_TIMEOUT;
  21.       validationTimeout = VALIDATION_TIMEOUT;
  22.       idleTimeout = IDLE_TIMEOUT;
  23.       initializationFailTimeout = 1;
  24.       isAutoCommit = true;
  25.       String systemProp = System.getProperty("hikaricp.configurationFile");
  26.       if (systemProp != null) {
  27.          loadProperties(systemProp);
  28.       }
  29.    }
  30. }
复制代码

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取两者中较小的那个值。

  1. //StatementUtil
  2. public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout) throws SQLException {
  3.    if (transactionTimeout == null) {
  4.      return;
  5.    }
  6.    if (queryTimeout == null || queryTimeout == 0 || transactionTimeout < queryTimeout) {
  7.      statement.setQueryTimeout(transactionTimeout);
  8.    }
  9. }
复制代码
接下来来看看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属性。

  1.   protected boolean createStreamingResultSet() {
  2.         return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
  3.         //getResultFetchSize默认值是0
  4.                 && (this.query.getResultFetchSize() == Integer.MIN_VALUE));
  5.     }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表