druidsockettimeout超时15分钟(转载)
背景
在应⽤端通过mybatis的interceptor⾃定义Plugin拦截Executor, 统计输出sql的执⾏耗时。
今天⽣产发⽣⼀个很奇怪的问题:莫名其妙卡顿15分钟+,其后正常返回sql正常结果!使⽤druid版本是1.0.2。。。。。
⽇志分析
统计发现:
1. 出现该情况的单量有6笔,集中在特定的2个⼩时之内,都是查询sql;都发⽣在1台应⽤服务器上。
2. 在这⼏笔订单卡住的时间内,轮询任务触发⼜正常查询成功并正确处理成功!
3. 数据库层⾯没有慢sql;且数据库实例的指标监控稳定,应⽤监控除sql耗时监控异常外,其他⼀切正常。
4. ⽇志没有输出error
推测是否是在getConnection过程中出现了等待。但因获取连接时的⽇志过少不到具体的原因!
DruidDataSource的testConnectionInternal
⽹上的类似问题说的原因是Druid在从连接池中获取的连接(代码详见:DruidDataSource的getConnectionDirect),在开启testWhileIdle时,如果活跃时间距离当前⽐较久,那么将验证该连接是否有效,⽆效会discardConnection并再次去pool内获取。
这个过程可能被数据库层⾯的防⽕墙策略已经关闭该连接,⽽客户端还傻傻的发sql验证直到超时。。。。
在该测试连接的⽅法中有⼀个查询超时超时时间:validationQueryTimeout。测试中是-1, 执⾏的是默认时间。。。。这个默认时间没有到!! (有到MappedStatementConfig由SqlMapConfiguration的defaultStatementTimeout)
深⼊理解JDBC的超时设置
应⽤与数据库间的timeout层级实例:
转帖:
上层的timeout依赖于下层的timeout,只有下层的timeout⽆误时,上层的timeout才能确保正常。eg. 当socket timeout出现问题时,上层的statement timeout 和transaction timeout都将失效。
statement timeout ⽆法处理⽹络连接失败时的超时,它能做的仅仅是限制statement的操作时间;⽹络连接失败时的timeout必须交由JDBC来处理;JDBC的socket timeout会受到操作系统socket timeout设置的影响。
1. Transaction Timeout:⼀般存在于框架(Spring, EJB)或应⽤级。spring在配置事务切⾯的时候可以配置timeout。
2. Statement Timeout:通过调⽤JDBC的java.sql.Statement.setQueryTimeout(int timeout) API进⾏设置。以myBatis为例,statement timeout的默认值可
以通过l中的defaultStatementTimeout 属性进⾏设置。同时,也可以设置sqlmap中select,insert,update标签的timeout属性,从⽽对不同sql语句的超时时间进⾏独⽴的配置。
3. JDBC的statement timeout:通过调⽤Connection的createStatement()⽅法创建statement来executeQuery时,会注册⼀个 TimerTask -
>CancelTask ⽤来给Timer -> CancelTimer()来处理timeout事项:发送 "KILL QUERY” 。代码详
见: StatementImpl 及 ConnectionImpl
4. JDBC的socket timeout:
1. 由于TCP/IP的结构原因,socket没有办法探测到⽹络错误,因此应⽤也⽆法主动发现数据库连接断开。如果没有设置socket timeout的
话,应⽤在数据库返回结果前会⽆期限地等下去,这种连接被称为dead connection。为了避免dead connections,socket必须要有超
时配置。socket timeout可以通过JDBC设置,socket timeout能够避免应⽤在发⽣⽹络错误时产⽣⽆休⽌等待的情况,缩短服务失效的
时间。不推荐使⽤socket timeout来限制statement的执⾏时长,因此socket timeout的值必须要⾼于statement timeout,否则,socket
timeout将会先⽣效,这样statement timeout就变得毫⽆意义,也⽆法⽣效。
eg. jdbc:
5. 操作系统的socket timeout配置
番外
在该⽂章中说Statement.setQueryTimeout : 如果设置的超时时间过长,在当⼤批量的SQL同时执⾏时,每⼀个SQL都会创建⼀个CancelTask对象,虽然很快执⾏完,且会调⽤CancelTask.cancel()⽅法,但是CancelTask⽅法的源代码仅仅是将⾃⼰的状态修改为:CANCELLED,⽽并不会直接从队列中移除这个对象, 导致OOM。建议在cancel()后需要purge⼀下。
解: MySQL Connector Java 5.1.44 版本的 StatementImpl 已经有了purge;且在1.8版本的JDK对Timer的cancel()也增加了移除queue内容
Timer
public int purge() {
timeout on t2 timerint result = 0;
synchronized(queue) {
for (int i = queue.size(); i > 0; i--) {
if ((i).state == TimerTask.CANCELLED) {
queue.quickRemove(i);
result++;
}
}
if (result != 0)
queue.heapify();
}
return result;
}
}
public void cancel() {
synchronized(queue) {
queue.clear();
}
类似问题
通过⽹上blog查发现有相似的问题存在:
1.
2.
3.
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论