如果你的项目重启数据库连接数突增!连接数据库超时!请收下这个方子

本文分析了数据库连接数突增及连接超时的原因,并提供了详细的解决方案,包括调整MySQL配置、使用正确的项目停止命令及优化连接池配置。

 

结论:

数据库连接数突增是数据库连接资源没有及时释放。

连接数据库超时是因为数据库连接资源释放的过早。

现象1:每次上线项目DB的连接数会突增。

原因:是项目关闭的时候没有释放连接资源导致。

DB的connection资源没有正常释放,导致项目启动的时候再次创建数据库连接资源,就出现了连接数突增的现象。一段时间后mysql根据wait_time的配置,自动回收conncetion,所以连接数又回落回来。

如果是是DB的connection资源没有正常释放,最可能的是在项目关闭的时候没有释放掉DB的连接资源。

经过在查看线上jekins的上线脚本后,发现线上停止项目使用的kill进程的方式来停止项目。那么就证明假设都成立了。接下来解决问题环节(程序员们喜闻乐见的百度和谷歌环节了)。

如何释放DB的连接资源,我列举了一下可选方案。

1.主动释放

项目关闭使用正确的stop命令,保证项目能正确的释放掉各种资源。
执行命令:xxxx_tomcat.stop

2.被动释放

如下两个配置搭配服用效果最佳
1.JDBC探活:每隔一段时间唤醒连接,保持长连接。
<property name="preferredTestQuery" value="SELECT 1"/> 
<property name="idleConnectionTestPeriod" value="300"/>
2.减少连接池内连接生存周期:
  2.1 使之小于mysql设置中的wait_timeout的值
  2.2 大于配置的探活的周期
  2.3 小于新项目上线的耗时,就可以在新项目上线过程中mysql自己回收连接资源避免连接突增的现象的发生。
<property name="maxIdleTime" value="1800" />

主要目的是尽可能的在新项目上线的同时,mysql自己就回收了连接资源。

现象2:连接数据库超时。

com.mysql.jdbc.CommunicationsException: 
The last packet successfully received from the server was58129 seconds ago.
The last packet sent successfully to the server was 58129 seconds ago, 
which is longer than the server configured value of 'wait_timeout'. 
You should consider either expiring and/or testing connection validity before
 use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' 
to avoid this problem. mysql

问题原因 :连接池里的connection资源mysql主动提前释放导致。

原因是在datasource连接池中配置的最大空闲时间到达之前(比如maxIdleTime,不通数据源配置名不一样),已经到达mysql的wait_timeout(最大空闲时间),是mysql主动把connection资源回收。但是项目中的连接池还持有connection,所以当项目中使用connection的时候会报CommunicationsException错误。

解决方案

1.修改mysql的wait_time,interactive_timeout把值调大(不建议如果太大,可能导致连接数较多,引起性能下降)

修改配置my.inf文件 
或者 
mysql命令set global interactive_timeout = 28800;set global interactive_timeout = 28800;

2.配置JDBC的重连机制autoReconnect(不建议,只有4.x版本,起作用)

jdbc:mysql://localhost:3306/test?user=root&password=&autoReconnect=true

3.减少连接池内的存活时间+JDBC探活(建议,搭配使用效果好)

JDBC探活:每隔一段时间唤醒连接,从而保持长连接
<property name="preferredTestQuery" value="SELECT 1"/> 
<property name="idleConnectionTestPeriod" value="300"/>

JDBC减少连接池内连接生存周期,**小于mysql配置wait_timeout 的值(很重要)***
<property name="maxIdleTime" value="1800" />   

最大闲置资源时间的配置

两个现象的解决方案都指向了同一个配置就是connection的最大闲置资源时间。

有两个地方可以配置最大闲置资源时间:

1.在项目的连接池中配置,比如maxIdleTime。

2.在mysql中也可以配置,interactive_timeout和wait_timeout。

 

三、MySql中的connection超时配置

mysql的配置中有interactive_timeout和wait_timeout两个参数,这两个参数有时候还存在覆盖的关系,所以还是给大伙说清楚一点两个的区别和联系方便大家理解。

建议interactive_timeout和wait_timeout参数值配置成一样的。

1.interactive_timeout和wait_timeout概念

mysql的连接超时时间配置
wait_timeout非交互式连接超时通过jdbc连接数据库是非交互式连接,最大闲置时间用于规定一个connection最大的空闲时间,默认是28800秒,超时MySQL会自动回收该connection。
interactive_timeout交互式连接超时通过mysql客户端连接数据库是交互式连接,最大闲置时间用于规定一个connection最大的空闲时间,默认是28800秒,超时MySQL会自动回收该connection。

 

 

 

 

2.修改配置参数的方式

1.修改配置文件my.ini

[mysqld]
wait_timeout = 20
interactive_timeout = 20

2.执行mysql命令

#修改global级别的配置
set global interactive_timeout = 10;
set global wait_timeout = 10;
#修改session级别的配置
set session  interactive_timeout=20;
set session  wait_timeout=20;

3.查看参数配置

mysql> show variables like '%timeout%';
+-----------------------------+----------+
| Variable_name               | Value    |
+-----------------------------+----------+
| connect_timeout             | 10       |
| delayed_insert_timeout      | 300      |
| have_statement_timeout      | YES      |
| innodb_flush_log_at_timeout | 1        |
| innodb_lock_wait_timeout    | 50       |
| innodb_rollback_on_timeout  | OFF      |
| interactive_timeout         | 28800    |
| lock_wait_timeout           | 31536000 |
| net_read_timeout            | 30       |
| net_write_timeout           | 60       |
| rpl_stop_slave_timeout      | 31536000 |
| slave_net_timeout           | 60       |
| wait_timeout                | 28800    |
+-----------------------------+----------+
13 rows in set (0.00 sec)

3.参数不同的继承关系

1.interactive_timeout和wait_timeout配置最终生效都是作用在session交互的时候生效。

2.控制最大空闲时间的参数是wait_timeout在起作用。不管是非交互式还是交互式连接,都是wait_timeout起作用

3.交互式连接下的wait_timeout和interactive_timeout配置都会继承自全局的interactive_timeout参数。

总结规律
  非交互,比如jdbc连接交互式,比如mysql客户端
global级别 查询session级别的配置查询session级别的配置
wait_timeout=26继承值wait_timeout=26 
interactive_timeout=31继承值interactive_timeout=31wait_timeout=31,interactive_timeout=31

 

 

 

 

 

1.wait_timeout决定连接超时时间的演示

因为我们是用mysql客户端连接,应该是交互式连接,连接超时起作用的应该是interactive_timeout参数,但是真是的这样吗。

确认设置连接空闲超时时间是WAIT_TIMEOUT

============= wait_timeout ================
mysql> set session WAIT_TIMEOUT=2;
Query OK, 0 rows affected (0.00 sec)
等待2秒再次查询,连接已经丢失,说明配置生效。
mysql> select 1;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    50
Current database: *** NONE ***

+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

============= interactive_timeout ================

mysql> set session  interactive_timeout=2;
Query OK, 0 rows affected (0.00 sec)
等待2秒再次查询,连接也没有丢失,说明配置未生效。
mysql> select 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

2.interactive_timeout不同的继承演示

设置interactive_timeout的配置
mysql> set global interactive_timeout=10;
Query OK, 0 rows affected (0.00 sec)

mysql> select variable_name,variable_value from information_schema.global_variables where variable_name in ('interactive_timeout','wait_timeout');
+---------------------+----------------+
| variable_name       | variable_value |
+---------------------+----------------+
| INTERACTIVE_TIMEOUT | 10             |
| WAIT_TIMEOUT        | 28800          |
+---------------------+----------------+
2 rows in set, 1 warning (0.00 sec)

新开一个窗口(交互式连接)查看,wait_timeout 和 interactive_timeout 都继承自global的interactive_timeout

交互式连接下可以看到interactive_timeout和wait_timeout都继承自全局INTERACTIVE_TIMEOUT
mysql> select variable_name,variable_value from information_schema.session_variables where variable_name in ('interactive_timeout','wait_timeout');
+---------------------+----------------+
| variable_name       | variable_value |
+---------------------+----------------+
| INTERACTIVE_TIMEOUT | 10             |
| WAIT_TIMEOUT        | 10             |
+---------------------+----------------+
2 rows in set, 1 warning (0.00 sec)

使用JDBC查询(非交互式)查看,wait_timeout继承自全局wait_timeout,interactive_timeout继承自全局interactive_timeout

Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/deeluma_01", "root", "root");
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select variable_name,variable_value from information_schema.session_variables where variable_name in ('interactive_timeout','wait_timeout')");
while(resultSet.next()){
	String variable_name = resultSet.getString("variable_name");
	int variable_value = resultSet.getInt("variable_value");
	System.out.println(variable_name+":"+variable_value);
}

//非交互式下查看,wait_timeout继承自全局wait_timeout,interactive_timeout继承自全局interactive_timeout
//===============//
INTERACTIVE_TIMEOUT:10
WAIT_TIMEOUT:28800

 

<think>我们正在处理用户关于SpringBoot应用使用HikariCP连接Oracle数据库连接数耗尽的问题。根据用户提供的引用资料,我们可以总结以下几点:1.连接池配置不当(如最大连接数过小、空闲连接存活时间等)可能导致连接耗尽。2.连接泄漏(即获取连接后没有正确释放)是常见原因。3.事务传播行为设置不合理可能导致连接占用时间过长。4.高并发场景下,如果连接池配置不足以支撑,也会导致连接耗尽。我们需要分析可能的原因并提供解决方案。步骤:1.检查连接池配置2.检查连接泄漏3.检查事务管理4.其他可能原因(如数据库连接超时设置、网络问题等)具体展开:一、连接池配置问题-最大连接数(maximum-pool-size)设置过小,无法应对并发求。-最小空闲连接数(minimum-idle)设置过小,导致在突增时来不及创建新连接。-连接超时时间(connection-timeout)设置过短,导致获取连接超时(虽然这不会直接耗尽连接,但会因超时导致获取连接失败,而实际连接可能被占用)。-连接最大生命周期(max-lifetime)设置过短,导致连接被频繁销毁和重建,增加连接池负担。-空闲连接存活时间(idle-timeout)设置过短,导致空闲连接被过早回收,当求到来时需要新建连接。二、连接泄漏-应用程序中获取数据库连接(如通过DataSource.getConnection())后,在使用完毕后没有调用close()方法释放回连接池。-即使使用了try-with-resources或finally块,也可能因为异常导致连接未关闭。-可以使用连接池的泄漏检测功能来定位。三、事务问题-长时间运行的事务会占用连接直到事务结束。-事务传播行为配置不当,可能导致一个事务跨越多个方法,延长了连接占用时间。四、其他原因-数据库本身的最大连接数限制:如果数据库设置的全局最大连接数较小,即使连接池配置较大,也会被数据库限制。-网络问题导致连接异常断开,而连接池未能及时销毁无效连接并重建。-应用负载过高,超出了连接池的处理能力。解决方案:1.调整连接池配置:根据引用[2]和[4],我们可以在application.properties或application.yml中配置HikariCP参数。例如:```properties#设置最大连接数(根据实际负载调整)spring.datasource.hikari.maximum-pool-size=50#最小空闲连接数spring.datasource.hikari.minimum-idle=10#连接超时时间(毫秒)spring.datasource.hikari.connection-timeout=30000#空闲连接存活时间(毫秒)spring.datasource.hikari.idle-timeout=600000#连接最大生命周期(毫秒)spring.datasource.hikari.max-lifetime=1800000#验证连接是否有效的查询(Oracle需要特定的查询,如SELECT1FROMDUAL)spring.datasource.hikari.connection-test-query=SELECT1FROMDUAL```2.检测和修复连接泄漏:-启用HikariCP的泄漏检测:```propertiesspring.datasource.hikari.leak-detection-threshold=60000#单位毫秒,表示连接超过60秒未关闭则视为泄漏```这样会在日志中打印泄漏警告,帮助定位未关闭的连接。-确保所有连接都在finally块或try-with-resources中关闭,例如:```javatry(Connectionconn=dataSource.getConnection();Statementstmt=conn.createStatement();ResultSetrs=stmt.executeQuery("SELECT...")){//处理结果集}//自动关闭资源```3.检查事务配置:-使用Spring的事务管理,避免手动控制事务边界。-检查事务传播行为,确保不会不必要地延长事务时间。-对于长时间事务,考虑拆分为多个小事务。4.检查数据库设置:-查看Oracle数据库的最大连接数(通过参数processes和sessions),确保足够。-如果数据库连接数不足,需要调整Oracle的初始化参数。5.监控连接池状态:-可以通过SpringBootActuator的`/actuator/metrics/hikaricp.connections`端点监控连接池指标。-或者通过日志记录连接池状态。6.其他优化:-如果应用确实需要很高的并发连接,考虑水平扩展应用实例。-优化SQL查询,减少单个查询的执行时间,从而释放连接更快。注意:Oracle数据库连接测试查询与MySQL不同,应使用`SELECT1FROMDUAL`。根据引用[3],我们知道连接池中的连接在调用close()时是释放回连接池,而不是真正关闭。因此,连接泄漏会导致连接无法回到池中,最终耗尽。总结排查步骤:1.检查HikariCP配置参数,特别是最大连接数。2.启用泄漏检测,检查是否有未关闭的连接。3.检查事务使用是否合理。4.监控数据库连接数,确认是否达到数据库上限。5.优化SQL和事务,减少连接占用时间。相关问题:1.如何监控HikariCP连接池的运行状态?2.在SpringBoot中如何配置多个数据源并分别使用HikariCP?3.如何优化Oracle数据库连接以提高性能?</think>### Spring Boot 应用使用 HikariCP 连接 Oracle 数据库连接数耗尽的排查与解决 #### 一、常见原因分析 1. **连接泄漏(未释放连接)** - 代码中获取连接后未调用 `Connection.close()`,导致连接无法归还池中[^3] - 异常场景下未在 `finally` 块中释放连接 - 示例风险代码: ```java Connection conn = dataSource.getConnection(); // 未关闭连接 Statement stmt = conn.createStatement(); stmt.executeUpdate("..."); ``` 2. **连接池配置不当** - `maximum-pool-size` 设置过小(如默认值 10)无法支撑高并发[^4] - `idle-timeout`/`max-lifetime` 过长导致无效连接占用资源 - Oracle 特有配置缺失(如未设置 `connection-test-query`) 3. **事务未及时提交** - 长事务(如批量处理)长时间占用连接 - 事务传播行为配置不当(如 `PROPAGATION_REQUIRES_NEW` 嵌套事务)[^1] 4. **数据库端限制** - Oracle 的 `PROCESSES`/`SESSIONS` 参数值过低 - 防火墙中断空闲连接但未通知应用层 5. **网络与性能问题** - SQL 执行效率低下(如全表扫描)延长连接占用时间 - 数据库服务器资源不足(CPU/内存) --- #### 二、解决方案 ##### 1. 启用连接泄漏检测(关键步骤) ```properties # application.properties spring.datasource.hikari.leak-detection-threshold=60000 # 60秒未关闭视为泄漏 ``` - 日志会打印 `Connection leak detected` 警告并显示调用栈[^3] - 修复建议: ```java // 正确写法 (try-with-resources) try (Connection conn = dataSource.getConnection(); PreparedStatement ps = conn.prepareStatement("...")) { // 业务操作 } // 自动调用 conn.close() ``` ##### 2. 优化 HikariCP 配置(Oracle 示例) ```properties # 基础配置 spring.datasource.url=jdbc:oracle:thin:@//host:1521/ORCLCDB spring.datasource.username=scott spring.datasource.password=tiger # HikariCP 调优 spring.datasource.hikari.maximum-pool-size=50 # 根据并发量调整 spring.datasource.hikari.minimum-idle=5 # 避免突发流量 spring.datasource.hikari.connection-timeout=30000 # 获取连接超时(ms) spring.datasource.hikari.idle-timeout=300000 # 空闲连接超时(5分钟) spring.datasource.hikari.max-lifetime=1200000 # 连接最大存活(20分钟) spring.datasource.hikari.connection-test-query=SELECT 1 FROM DUAL # Oracle 健康检查[^2] ``` ##### 3. 事务优化策略 - 将 `@Transactional` 注解置于最小化代码块 - 避免在事务中执行远程调用或耗时操作 - 使用只读事务提升并发: ```java @Transactional(readOnly = true) public List<User> queryUsers() { ... } ``` ##### 4. 数据库层检查 ```sql -- 检查 Oracle 连接数限制 SELECT * FROM v$resource_limit WHERE resource_name IN ('processes','sessions'); -- 查看当前活跃连接 SELECT sid, serial#, username, status FROM v$session WHERE type='USER'; ``` - 若接近上限,需 DBA 调整参数: ```sql ALTER SYSTEM SET processes=500 SCOPE=SPFILE; ``` ##### 5. 监控与诊断工具 - **HikariCP 原生监控**: ```java HikariPoolMXBean pool = dataSource.getHikariPoolMXBean(); System.out.println("活跃连接: " + pool.getActiveConnections()); System.out.println("空闲连接: " + pool.getIdleConnections()); ``` - **Spring Boot Actuator**: ```properties management.endpoints.web.exposure.include=health,metrics,prometheus management.metrics.enable.hikaricp=true ``` 访问 `/actuator/metrics/hikaricp.connections` 查看实时指标 --- #### 三、预防措施 1. **代码规范** - 使用 Spring JdbcTemplate 或 ORM 框架(自动管理连接) - 禁止在循环中手动获取连接 2. **压力测试** - 使用 JMeter 模拟高并发场景 - 监控连接池利用率: $$ \text{利用率} = \frac{\text{Active Connections}}{\text{Maximum Pool Size}} \times 100\% $$ 3. **失效连接处理** ```properties spring.datasource.hikari.keepaliveTime=30000 # 每30秒保活检测 spring.datasource.hikari.validation-timeout=5000 # 验证超时 ``` > 通过以上组合策略,可解决 90% 以上的连接耗尽问题。若仍出现泄漏,需结合 `leak-detection-threshold` 日志定位具体代码位置[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值