0
点赞
收藏
分享

微信扫一扫

深入理解JDBC原理和timeout配置

ZGtheGreat 2021-09-30 阅读 59
技术文集

本文来源于Woon Duk Kang的一篇文章的学习理解,也参考了国内网友的转载和翻译,在此一并谢过大神们的智力劳动。

一些理解上的前提

应用 ----> MySql ,这样一个访问关系,可以看作是mysql-java-connector这个client与mysql server之间的cs架构,jdbc就是两者之间通信的协议,当然它也是基于TCP协议的。具体在java里用的是BIO socket,也就是同步阻塞的io方式。

先捋一下应用里执行了一个sql查询,大致发生了什么:

  1. 一个sql查询,比如select 1 from dual ,先是需要从客户端跟mysql之间建立tcp,这个当然是利用os里边的tcp模块来做的、3次握手、tcp连接建立。

  2. 然后就是mysql jdbc协议的连接建立、也是要有一个“握手”的动作的,至此,jdbc连接建立。

  3. 然后客户端使用jdbc协议发送查询语句请求给服务端,可以理解为send("select 1 from dula")

  4. 服务端也就是数据库服务收到请求后,执行sql,然后返回结果给客户端。

引用文章的重点在于辨析几个超时时间:

  • spring框架来负责的事务超时

  • mysql客户端驱动程序的应用层来负责的statement超时

  • mysql客户端驱动程序的网络层来负责的jdbc socket超时

  • os的tcp模块负责的socket超时

1、Transaction超时和statement超时

如何预估transaction超时

事务超时 = Statement Timeout * N (number of statements being processed) + @ (garbage collection, etc.)

所以事务超时设置的时候要比N个statement超时再多一点,比如,假设一个事务里有5条 Statement ,每条 Statement 执行时间是200毫秒,其它业务逻辑或框架操作的执行时间是100毫秒,那事务允许的超时时间至少应该1100毫秒(200 * 5 + 100)。

如何启用事务超时

在 Spring 中,事务超时可以在 XML 文件显式配置或在 Java 代码中用 Transactional 注解来配置。

事务超时的作用以及原理

执行每一个statement时,首先判断是否statement超时,如果没有超时,那么判断校验保存在ThreadLocal里的连接的事务开始时间和超时时间,这可以防止每个statement不长时间,但是由于语句太多,造成整个事务太长的情况。

如何启用statement超时

Statement 超时是通过 JDBC API 中的java.sql.Statement.setQueryTimeout(int timeout)方法配置的。不过现在的开发者已经很少直接在代码中配置它了,更多是通过框架来进行设置。

以 iBatis 为例,可以通过 SqlMapConfig.xml 中的 setting 属性defaultStatementTimeout 来设置全局的 statement 超时缺省值。你也可以通过在具体的 sql 映射文件中的 select insert update 标签的 statement 属性来覆盖。

statement超时的原理

MySql里边,或者准确说是mysql客户端、驱动,里边控制statement的方法是,当执行一条语句时,会创建一个超时处理线程timeout-execution,它负责当发生超时的时候创建一个相同配置的连接,然后用这个连接去发送cancel命令给数据库。完整流程如下:

QueryTimeout for MySQL JDBC Statement (5.0.8)

  1. Creates a statement by calling Connection.createStatement().
  2. Calls Statement.executeQuery().
  3. The statement transmits the Query to MySqlServer by using the internal connection.
  4. The statement creates a new timeout-execution thread for timeout process.
  5. For version 5.1.x, it changes to assign 1 thread for each connection.
  6. Registers the timeout execution to the thread.
  7. Timeout occurs.
  8. The timeout-execution thread creates a connection that has the same configurations as the statement.
  9. Transmits the cancel Query (KILL QUERY "connectionId“) by using the connection.

2、网络层面的jdbc socket timeout和os tcp timeout

spring.datasource.url =jdbc:mysql://mysql_ip:3306/db_name?socketTimeout=60000&connectTimeout=30000&useUnicode=true&characterEncoding=utf-8&useSSL=false

在spring数据源配置jdbc连接串的时候,要配置上connectTimeout和socketTimeout,因为这俩默认是0,也就是永远不超时。这俩参数的含义很容易理解,连接建立超时时间和读写超时时间。(想知道这俩的重要性,尝试不配置然后重启下数据库试试...重启完了之后数据库主机上netstat还能查到之前的连接吗)

几种情形:

  • 网络故障或者数据库主机挂了,tcp无法建立,connection timeout

  • 数据库服务挂了,tcp也是无法建立,网络通的但是没有服务监听端口了。跟上边一样,connection timeout

  • 请求个sql查询,结果因为慢查询或者什么原因,一直不返回,那么socket timeout

下面展示了 Socket 超时设置的两个选项,其配置因不同的驱动而异。

  • Socket 连接时的超时:通过 Socket 对象的 connect(SocketAddress endpoint, int timeout)方法来配置
  • Socket 读写时的超时:通过 Socket 对象的 setSoTimeout(int timeout)方法来配置

最后,是os层面的tcp超时时间

如果 Linux 服务器的TCP KeepAlive 检查周期配置为30分钟,那么即使应用程序里将 Socket 超时设置为0,由网络原因引起的数据库网络连接问题也不会超过30分钟。

通常,应用程序会在调用 Socket 的 read() 方法时由于网络问题而阻塞住。然而很少在调用 Socket 的 write() 方法时处于等待状态,这取决于网络构成和错误类型。当应用程序调用 Socket 的 write() 方法时,数据被记录到操作系统的内核缓冲区,然后将控制权立即交还给应用程序。因此,一旦数据已经写入内核缓冲区,write() 的调用始终是成功。但是,如果操作系统内核缓冲区由于特殊的网络错误而满了的话,write() 方法也会进入等待状态。这种情况下,操作系统会尝试重新发送数据包一段时间,并在达到超时限制时产生错误。 在公司的 Linux服务器上这种情况的超时时间设置为15分钟。

参考文章

原文:
http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/
By Woon Duk Kang, Software Engineer at Web Platform Development Lab, NHN Corporation.

英文转载:
https://blog.csdn.net/weixin_40805079/article/details/86543366
https://my.oschina.net/swearyd7/blog/547307

中文翻译:
https://www.jianshu.com/p/6d19e0d7f81c

网上查了一下,Woon Duk Kang是韩国人,PinPoint的作者。

举报

相关推荐

0 条评论