druid使用入坑记(链接缓慢,hold问题)

2024-05-27 06:58

本文主要是介绍druid使用入坑记(链接缓慢,hold问题),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        在工作中,出现使用Druid链接Postgresql数据库,当第一次动态链接数据库源或者连过长时间不使用数据库时,出现链接过程缓慢,甚至hold的情况,链接时间以分计算,导致前端接口响应缓慢,影响用户操作问题。

        经过日志的排查,发现是在DruidDataSource对象链接数据源过程中卡主了,并不是执行SQL语句查询慢的原因,通过网上查询资料,从一下几方面进行改进和问题排查。

        1、是否存在数据源配置文件链接URL地址,host配置问题

        2、是否可以通过修改数据源存活时间或者使用定时心跳保证链接长时间存活

        3、是否能够通过数据源配置文件链接其他参数进行配置,避免该问题

        4、需要通过Druid框架源码分析问题的主要原因,可能更Linux系统、防火墙、数据库服务相关参数存在一定的关系。

        通过Druid社区翻阅相关帖子,发现了和我相同问题的帖子,https://www.oschina.net/question/1450045_2157629?sort=time

帖子中通过程序日志和druid的统计日志进行分析锁定问题在获取链接时或者进行testWhileIdle空闲检验过程中。更多参考文章

https://blog.csdn.net/aiyo92/article/details/86540647

https://www.oschina.net/question/1450045_2157629?sort=time
https://www.oschina.net/question/1450045_2157160
https://my.oschina.net/u/3434392/blog/3017866
https://www.oschina.net/question/2433836_2148757
https://www.oschina.net/question/1034044_2162691?sort=default
https://my.oschina.net/u/3434392/blog/3017866

最后发现是socketTimeout的参数设置的问题,可以通过linux服务器设置,数据源链接也可以设置

jdbc timeout类别

主要有如下几个类别

 

  • transaction timeout 设置的是一个事务的执行时间,里头可能包含多个statement
  • statement timeout(也相当于result set fetch timeout) 设置的是一个statement的执行超时时间,即driver等待statement执行完成,接收到数据的超时时间(注意statement的timeout不是整个查询的timeout,只是statement执行完成并拉取fetchSize数据返回的超时,之后resultSet的next在必要的时候还会触发fetch数据,每次fetch的超时时间是单独算的,默认也是以statement设置的timeout为准)
  • jdbc socket timeout 设置的是jdbc I/O socket read and write operations的超时时间,防止因网络问题或数据库问题,导致driver一直阻塞等待。(建议比statement timeout的时间长)
  • os socket timeout 这个是操作系统级别的socket设置(如果jdbc socket timeout没有设置,而os级别的socket timeout有设置,则使用系统的socket timeout值)。

上面的不同级别的timeout越往下优先级越高,也就是说如果下面的配置比上面的配置值小的话,则会优先触发timeout,那么相当于上面的配置值就”失效”了。

jdbc socket timeout

 

这个不同数据的jdbc driver实现不一样

mysql

jdbc:mysql://localhost:3306/ag_admin?useUnicode=true&characterEncoding=UTF8&connectTimeout=60000&socketTimeout=60000

通过url参数传递即可

pg

jdbc:postgresql://localhost/test?user=fred&password=secret&&connectTimeout=60&socketTimeout=60

pg也是通过url传递,不过它的单位与mysql不同,mysql是毫秒,而pg是秒

oracle

oracle需要通过oracle.jdbc.ReadTimeout参数来设置,连接超时参数是oracle.net.CONNECT_TIMEOUT

  • 通过properties设置 Class.forName("oracle.jdbc.driver.OracleDriver"); Properties props = new Properties() ; props.put( "user" , "test_schema") ; props.put( "password" , "pwd") ; props.put( "oracle.net.CONNECT_TIMEOUT" , "10000000") ; props.put( "oracle.jdbc.ReadTimeout" , "2000" ) ; Connection conn = DriverManager.getConnection( "jdbc:oracle:thin:@10.0.1.9:1521:orcl" , props ) ;
  • 通过环境变量设置 String readTimeout = "10000"; // ms System.setProperty("oracle.jdbc.ReadTimeout", readTimeout); Class.forName("oracle.jdbc.OracleDriver"); Connection conn = DriverManager.getConnection(jdbcUrl, user, pwd); 注意需要在connection连接之前设置环境变量
  • tomcat jdbc pool 一般我们不直接使用jdbc connection,而是使用连接池。由于tomcat jdbc pool是springboot默认使用的数据库连接池,这里就讲述一下如何在tomcat jdbc pool下设置。 spring.datasource.tomcat.connectionProperties=oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000 注意,这里是分号分隔,单位是毫秒,这里可以根据各自的情况配置前缀(tomcat jdbc连接池的话,默认是spring.datasource.tomcat),可以自定义,比如 @Bean @Qualifier("writeDataSource") @ConfigurationProperties(prefix = "spring.datasource.write") public DataSource writeDataSource() { return DataSourceBuilder.create().build(); } 假设你这里是自定义了prefix为spring.datasource.write,那么上述配置就变为 spring.datasource.write.connectionProperties=oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000 oracle.jdbc.ReadTimeout如果没有设置的话,driver里头默认是0

oracle.jdbc.ReadTimeout

driver内部将该值设置到oracle.net.READ_TIMEOUT变量上

  • oracle.net.nt.TcpNTAdapter
    @Overridepublic void setReadTimeoutIfRequired(final Properties properties) throws IOException, NetException {String s = ((Hashtable<K, String>)properties).get("oracle.net.READ_TIMEOUT");if (s == null) {s = "0";}this.setOption(3, s);}public void setOption(int var1, Object var2) throws IOException, NetException {String var3;switch(var1) {case 0:var3 = (String)var2;this.socket.setTcpNoDelay(var3.equals("YES"));break;case 1:var3 = (String)var2;if(var3.equals("YES")) {this.socket.setKeepAlive(true);}case 2:default:break;case 3:this.sockTimeout = Integer.parseInt((String)var2);this.socket.setSoTimeout(this.sockTimeout);}}

可用看到最后设置的是socket的soTimeout

实例

    @Testpublic void testReadTimeout() throws SQLException {Connection connection = dataSource.getConnection();String sql = "select * from demo_table";PreparedStatement pstmt;try {pstmt = (PreparedStatement)connection.prepareStatement(sql);ResultSet rs = pstmt.executeQuery();int col = rs.getMetaData().getColumnCount();System.out.println("============================");while (rs.next()) {for (int i = 1; i <= col; i++) {System.out.print(rs.getObject(i));}System.out.println("");}System.out.println("============================");} catch (SQLException e) {e.printStackTrace();}}

超时错误输出

//部分数据输出
java.sql.SQLRecoverableException: IO 错误: Socket read timed outat oracle.jdbc.driver.T4CPreparedStatement.fetch(T4CPreparedStatement.java:1128)at oracle.jdbc.driver.OracleResultSetImpl.close_or_fetch_from_next(OracleResultSetImpl.java:373)at oracle.jdbc.driver.OracleResultSetImpl.next(OracleResultSetImpl.java:277)at com.example.demo.DemoApplicationTests.testReadTimeout(DemoApplicationTests.java:68)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: oracle.net.ns.NetException: Socket read timed outat oracle.net.ns.Packet.receive(Packet.java:339)at oracle.net.ns.DataPacket.receive(DataPacket.java:106)at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:315)at oracle.net.ns.NetInputStream.read(NetInputStream.java:260)at oracle.net.ns.NetInputStream.read(NetInputStream.java:185)at oracle.net.ns.NetInputStream.read(NetInputStream.java:102)at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:124)at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:80)at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1137)at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:290)at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)at oracle.jdbc.driver.T4CPreparedStatement.fetch(T4CPreparedStatement.java:1119)... 35 more

刚开始会有数据输出,但是到了某个resultSet的next的时候,报了超时(close_or_fetch_from_next),这个超时指定的是当result.next方法触发新的一批数据的拉取(当一个fetchSize的数据消费完之后,接下来的next会触发新一批数据的fetch)之后在timeout时间返回内没有收到数据库返回的数据。 oracle的jdbc默认的fetchSize为10,也就是每个fetch,如果超过指定时间没接收到数据,则抛出timeout异常。

小结

jdbc的socketTimeout值的设置要非常小心,不同数据库的jdbc driver设置不一样,特别是使用不同连接池的话,设置也可能不尽相同。对于严重依赖数据库操作的服务来说,非常有必要设置这个值,否则万一网络或数据库异常,会导致服务线程一直阻塞在java.net.SocketInputStream.socketRead0。

  • 如果查询数据多,则会导致该线程持有的data list不能释放,相当于内存泄露,最后导致OOM
  • 如果请求数据库操作很多且阻塞住了,会导致服务器可用的woker线程变少,严重则会导致服务不可用,nginx报504 Gateway Timeout

doc

  • oracle.jdbc.ReadTimeout
  • 深入理解JDBC的超时设置
  • 在Spring中基于JDBC进行数据访问时如何控制超时
  • BugFix-HttpURLConnection

这篇关于druid使用入坑记(链接缓慢,hold问题)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1006766

相关文章

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

oracle DBMS_SQL.PARSE的使用方法和示例

《oracleDBMS_SQL.PARSE的使用方法和示例》DBMS_SQL是Oracle数据库中的一个强大包,用于动态构建和执行SQL语句,DBMS_SQL.PARSE过程解析SQL语句或PL/S... 目录语法示例注意事项DBMS_SQL 是 oracle 数据库中的一个强大包,它允许动态地构建和执行