“自以为对的”MyBatis空闲连接探测的机制

开发 前端
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种处理方式很流行,能使并发 Web 应用快速响应请求。

 [[392379]]

最近碰到个现象,某个应用,每天在21:00-23:00才会执行,连接数据库执行操作,间隔性出现连接超时的错误,

  1. Connection timed out (Read failed) 

因为应用和数据库是跨网段,咨询了下,防火墙超时时间配置的是30分钟,应用用的MyBatis连接池,相关配置如下,

相关参数解释,如下所示,

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到UNPOOLED下的属性外,还有更多属性用来配置POOLED的数据源:

poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10

poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。

poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)

poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。

poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。默认值:3(新增于 3.4.5)

poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。

poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。

poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

P.S.

https://mybatis.org/mybatis-3/zh/configuration.html#environments

按照这字面意思,一开始我们理解poolPingConnectionsNotUsedFor参数控制的是连接多久没用,即处于空闲状态,在参数poolPingEnabled开启时,就会执行poolPingQuery定义的SQL主动探测。

如果按照这理解,poolPingConnectionsNotUsedFor设置了3000,即3秒,远小于30分钟防火墙超时的设置,不应该出现连接超时的现象。

我们怀疑过防火墙的配置,但从应用端看,并不是所有的请求都超时,而且防火墙端,没看到什么异常。数据库层,应该未设置过相关的配置。

原因是什么?

作为一款成熟的产品,不太可能因为bug,更多还是对他的理解存在偏差。

下载3.3.0的源码,链接如下,

https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.3.0

搜索这几个参数所在的文件,找到了PooledDataSource类,可以看到这三个参数都设置了初始值,

看下这个pingConnection方法,

如果连接未关闭,判断逻辑如下,

1. poolPingConnectionsNotUsedFor的值>=0;

2. getTimeElapsedSinceLastUse()>poolPingConnectionsNotUsedFor;

getTimeElapsedSinceLastUse()定义如下,

lastUsedTimestamp是在构造函数PooledConnection中定义的,

PooledConnection会在获取连接(popConnection)和回收连接(pushConnection)的时候调用,获取连接和回收连接则会被getConnection()和invoke()调用,因此,(2)的意思是当前这个连接空闲的时间是否大于这个参数poolPingConnectionsNotUsedFor定义的时间。

3. 如果满足条件(1)和(2),则会执行poolPingQuery的SQL,此处就是"select 1 from dual",如果执行失败,会关闭这个连接,

从应用日志,能看到这些信息,

  1. Testing connection 0000000000 ... 
  2. Execution of ping query 'select 1 from dual' failed:  Connection timed out (Read failed) 

这个问题的关键,就是这个pingConnection,在什么时候调用,就决定了poolPingConnectionsNotUsedFor什么时候起作用,可以看到,他是在这个isValid的方法中调用的,

而这个isValid是在每次获取连接和回收连接时调用的,换句话说,他是被动调用,并不是我们认为的空闲时主动调用,所以这个应用,只是晚上会跑,空闲连接超过30分钟是很正常的,

应用开了debug,这两段之间的间隔时间,就是得到超时连接的时间,

经过单线程测试,大约在15分钟,

因此,对这种testOnBorrow的连接探测机制,各有优缺点,优点就是会在一定程度保证应用正常的业务请求得到可用的连接,毕竟不可用的连接都已经被poolPingQuery定义的SQL测试了,一般情况下,不会让正常的业务请求出现报错,除非连接池没任何可用的连接。缺点就是如果配置的poolPingConnectionsNotUsedFor很小,某些请求都会在执行之前先进行验证,但是换个角度,如果是高并发,只要参数不是0,一般可能都不会满足需要验证的条件,如果设置为0,就可能会有很多pingQuery定义的SQL执行。而且,如果像上述单线程的操作,他会一个连接一个连接的尝试,等待一个连接出现超时错误的时间间隔是15分钟,这就很低效了。

对连接池的选择和配置,确实得结合实际场景需求来决策。

通过这个问题,至少让我明白,“自以为对的”机制正确还是错误,还是看他的实现,这才是最可靠的验证,而且,通过他的逻辑,可以让我们借鉴一些设计路径,多考虑他这么做背后的意义和影响,更有助我们将其用到正确的场景。

 

责任编辑:武晓燕 来源: bisal的个人杂货铺
相关推荐

2017-08-17 17:09:28

PostgreSQL 数据块机制

2009-07-23 08:40:37

VMware迁移备份归档

2009-06-18 14:51:12

Hibernate缓存Hibernate

2020-07-23 10:00:50

AI 数据人工智能

2018-11-07 10:18:33

2012-02-23 09:58:23

2024-03-04 10:00:35

数据库处理机制

2011-04-07 14:29:56

2015-12-14 10:20:57

Python程序员错误

2020-10-27 06:56:53

IoT产品实践

2010-10-08 10:42:30

2020-12-30 07:49:32

KubernetesJava Spring Clo

2021-07-27 07:12:11

Getter接口Setter

2022-10-20 15:58:51

人工智能研究

2011-07-18 08:57:13

MySQLwait_timeouDBCP

2021-11-23 15:40:36

人工智能AI

2021-08-16 11:26:07

物联网工业 4.0IoT

2015-10-08 08:46:29

职业建议

2010-10-26 17:17:37

创建ORACLE索引

2020-09-25 10:14:54

漏洞
点赞
收藏

51CTO技术栈公众号