深入理解数据库编程中的超时设置
【转载声明】本文为转载文章,原作者「田守枝」,版权归原作者所有。 原文链接:https://mp.weixin.qq.com/s/qQSIPMM_-NGXgaX3V4-oEQ
公众号:杨建荣的学习笔记 · 作者:田守枝 · 发布:2021-08-10 23:49:21 · 原文链接
点击上方”田守枝的技术博客”,关注我
数据库是开发过程中最常用的组件,然而我们经常会遇到各种各样的超时异常,如:
-
connect timeout:建立数据库连接超时
-
socket timeout:socket读取超时
-
statement timeout:单个sql执行超时
-
transaction timeout:事务执行超时,一个事务中可能包含多个sql
-
get connection timeout:从连接池中获取链接超时
读完此文,你将彻底掌握各种超时产生的根本原因,以及对应的解决方案。
1 connectTimeout与socketTimeout
connect timeout和socket timeout都属于TCP层面的超时。
以mysql为例,我们可以在jdbc url中指定connectTimeout和socketTimeout。如:
jdbc:mysql://localhost:3306/db?connectTimeout=1000&socketTimeout=60000
其中:
-
connectTimeout:表示的是数据库驱动(mysql-connector-java)与mysql服务器建立TCP连接的超时时间。
-
socketTimeout:是通过TCP连接发送数据(在这里就是要执行的sql)后,等待响应的超时时间。
mysql驱动(mysql-connector-java)在与服务端建立Socket连接时,会将这两个参数设置到socket对象上参见:
com.mysql.jdbc.MysqlIO类的构造方法:

提示:这里的mysqlConnection类型为java.net.Socket
如果这两个参数设置的不够合理,都会导致mysql驱动抛出以下异常:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
相信大部分读者对这个异常都不陌生。接下来笔者将分别演示这两个异常是如何产生的,并提出对应的解决方案。
1.1 connectTimeout
下面首先通过一个案例演示如何模拟connectTimeout
@Testpublic void testConnectTimeout() throws SQLException { DruidDataSource dataSource = new DruidDataSource(); dataSource.setInitialSize(5); dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?connectTimeout=5"); dataSource.setUsername("root"); dataSource.setPassword(“your password"); dataSource.setDriverClassName("com.mysql.jdbc.Driver”); dataSource.init();//初始化,底层通过mysql-connector-java建立数据库连接}
笔者这里将connectTimeout设置为了5ms,表示mysql驱动与服务端建立一个连接最多不能超过5ms。由于这里是与本地(127.0.0.1)数据库建立一个连接,5ms已经足够。然而,如果你是与一个远程数据库建立连接,那么5ms可能无法完成建立一个连接,此时你极有可能会遇到类似以下异常:
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failureThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ...Caused by: java.net.SocketTimeoutException: connect timed out at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ...
到这里,我们看到了:
CommunicationsException异常,异常的Caused by部分是
java.net.SocketTimeoutException: connect timed out
也就是说,建立底层socket 连接超时了。这通常意味着我们需要将connectTimeout值调大。
这个问题并非无关紧要,特别是在公司有多个数据中心的情况下,尤其需要注意。笔者曾经遇到过有业务开发同学,应用部署在北京,数据库集群在北京和上海都有部署,如下图:

