金歌 发表于 2024-10-20 03:27:29

数据库超时排查

背景:

   项目是用的springboot,毗连池用的是hikaricp,且数据库毗连做了LB设置,之前就是常常会有数据库出现标题,专家给到的解决方案。
数据毗连io超时报错,排查了当时数据库各项指标都无显示非常,且也没有获取到当时的queryId,给出的解决方案是增加重试机制 ,但是成本太高,故本身根据日期排查下标题,日志如下

https://i-blog.csdnimg.cn/direct/a13a3cdb8ae44a3ca8dbab7bc3afcc69.png

错误信息:

org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.<MSG>Read timed out
这条日志信息表明在与PostgreSQL数据库通讯时发生了I/O错误,具体是读操作超时 (Read timed out)。
具体调用栈跟踪:

at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:383)
这个调用来自PostgreSQL JDBC 驱动程序,具体是在实行查询的时候抛出的非常。

at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:496)
位于 PgStatement 类,该类负责实行SQL语句。

at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:413)
这个方法负责实行平凡的SQL语句。

at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:190)
表示这是通过PreparedStatement实行的SQL语句。

at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:152)
具体实行的是更新操作(executeUpdate 表示是INSERT、UPDATE或DELETE等更新操作)。
           JDBC4的isValid方法来测试毗连是否可用,是通过向数据库服务器发送一个ping哀求来实现的。这个ping哀求的实现方式大概因数据库厂商而异,但通常包罗向数据库服务器发送一个简朴的网络数据包,以测试毗连是否正常。
        JDBC4的isValid方法的原理是基于底层网络毗连的有用性进行检测,它利用了底层协议的心跳机制来检测毗连的有用性。当调用isValid方法时,JDBC驱动程序会发送一个心跳包到数据库服务器,等候数据库服务器的响应。假如在指定的超时时间内收到了响应,则以为毗连是有用的,否则以为毗连已经失效。 
at org.postgresql.jdbc.PgConnection.isValid(PgConnection.java:1465)
这里可以看到驱动程序在检查毗连是否有用。(到这里就可以定位是校验获取的毗连有用性出了标题,也就可以明确了当时为什么查不到queryId了)

at com.zaxxer.hikari.pool.PoolBase.isConnectionAlive(PoolBase.java:161)
HikariCP毗连池在检查某个毗连是否仍然活跃。

at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:186)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:100)
HikariCP毗连池尝试获取一个毗连。这个毗连大概因为前述错误而失败。

at com.baomidou.dynamic.datasource.ds.ItemDataSource.getConnection(ItemDataSource.java:56)
at com.baomidou.dynamic.datasource.ds.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:48)
显示利用的是动态数据源管理工具(比方MyBatis-Plus Dynamic Datasource),尝试从HikariCP毗连池获取毗连。

at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:159)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:117)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
Spring JDBC 工具类尝试获取毗连,从而对数据库进行操作。

at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:80)
at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
MyBatis在Spring管理的事务中打开一个毗连。同样反映出当前利用了Spring事务管理。

at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:345)
at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.prepareStatement(MybatisSimpleExecutor.java:93)
MyBatis实行器获取毗连并准备实行SQL语句。

at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doQuery(MybatisSimpleExecutor.java:68)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:333)
MyBatis实行查询操作,并从数据库中获取效果。
总结

从上述日志信息中,我们可以看到操作流程如下:

[*]应用程序利用 MyBatis 和 Spring 获取数据库毗连。
[*]利用 HikariCP 作为毗连池管理数据库毗连。
[*]在 HikariCP 中检查毗连是否有用时,发生了 I/O 错误(读超时)。
[*]这导致 PostgreSQL 驱动程序抛出 PSQLException 非常。
这通常表示网络标题、数据库设置标题或者客户端设置标题。需要进一步检查网络毗连、数据库以及毗连池的干系设置来排查具体缘故原由。


具体的缘故原由没定位出来,但是现在可以通过设置提升用户体感

hikari:

        # 数据库连接有效性校验超时时间(ms)默认是5秒

        validation-timeout: 500








HikariCP 是怎么检查毗连是否有用

HikariCP 是一个高性能的 JDBC 毗连池,在管理数据库毗连的过程中,它提供了一些机制来检查毗连是否有用,以包管毗连的可用性和稳定性。下面将具体先容 HikariCP 如何检查毗连是否有用:
1. Connection Test Options

HikariCP 提供了几个参数来设置毗连的检测和验证,这些设置项资助确保毗连池中的毗连是可用的:


[*]connectionTestQuery    测试语句,不保举设置  
[*]validationTimeout      默认5秒   验证是否有用的超时时间

[*]最小设置为1秒毫秒数据会转为秒
[*]final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;

[*]idleTimeout       默认10分钟

[*]这个属性控制毗连池中空闲毗连的最大空闲时间,只有当毗连池中毗连数目大于最小毗连数目(minimumIdle)时会收效
 

[*]maxLifetime        默认30分钟

[*]这个属性控制毗连池中一个毗连的最大生存时间,当一个毗连的生存时间大于这个值且没有正在被利用时,将会被关掉 与idleTimeout区别
max-lifetime控制毗连的总的生命周期,无论当前毗连数是否大于最小毗连数目,都会关掉生命周期完结的毗连,idle-timeout只控制空闲且大于最小毗连数目的那部分毗连

[*]connectionTimeout    默认30秒   

[*] 此属性控制客户端等候来自毗连池的毗连的最大毫秒数。假如高出这个时间而没有毗连可用,将抛出SQLException。最低可担当的毗连超时时间是250毫秒。默认值:30000(30秒)

[*] leakDetectionThreshold      默认0  不开启毗连泄漏检测 
2. 无需显式设置的默认活动

HikariCP 默认情况下利用 JDBC 驱动程序提供的 isValid 方法来验证毗连的有用性。isValid 方法通过尝试与数据库进行简朴的通讯验证毗连是否有用。
3. 设置 connectionTestQuery

在某些情况下,具体的 JDBC 驱动程序大概不支持 isValid 方法,或者你想利用自定义的查询语句来验证毗连。这时可以利用 connectionTestQuery 参数。
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/yourdb");
config.setUsername("yourusername");
config.setPassword("yourpassword");
config.setConnectionTestQuery("SELECT 1");
HikariDataSource dataSource = new HikariDataSource(config);

4. 工作方式

当 HikariCP 需要验证毗连时,它会实行以下步调:

[*] 默认利用 isValid 方法:
boolean isValid = connection.isValid(validationTimeout);
这个方法会利用数据库驱动提供的 isValid 方法,可以设置 validationTimeout 来指定超时。假如毗连在规定时间内响应,那么这个毗连被以为是有用的。
[*] 利用自定义 connectionTestQuery:
假如设置了 connectionTestQuery,HikariCP 会实行该查询来验证毗连。这个查询应该是快速并且无副作用的,比方 SELECT 1。假如实行成功,这个毗连被以为是有用的。具体代码逻辑如下:
try (Statement statement = connection.createStatement()) {
    statement.executeQuery(connectionTestQuery);
    // If the query executes successfully, the connection is valid
} catch (SQLException e) {
    // If query execution fails, the connection is considered invalid
}

5. 利用 validationTimeout

HikariCP 提供了 validationTimeout 参数来设置毗连验证的超时时间:
config.setValidationTimeout(5000);// 设置验证超时时间为5秒

6. 毗连池中的毗连检查周期

HikariCP 在以下情况会进行毗连检查:


[*]获取新毗连:当应用哀求新毗连时,先检查当前毗连是否有用。
[*]闲置毗连:利用 idleTimeout 设置检查闲置毗连是否应该被移除。
[*]最大毗连生命周期:利用 maxLifetime 设置确保毗连在规定的生命周期内利用,高出时间一律关闭,以制止埋伏的资源泄漏或者某些数据库的限制。
config.setIdleTimeout(600000);      // 闲置10分钟后移除连接
config.setMaxLifetime(1800000);   // 连接最大生命周期为30分钟
config.setConnectionTimeout(30000); // 连接超时设置为30秒


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 数据库超时排查