连接池 - 为什么要在签入时测试连接?

Connection Pooling - Why test connections on checkin?

你好,这个问题应该适用于比 c3p0 更多的连接池管理器,但我将使用那个作为例子。

c3p0 为连接池管理提供的一个选项是在将连接签入连接池之前测试连接。 这是否意味着在应用程序使用连接之后?如果是这样,那么如果连接只是被应用程序使用,它不是一直有效吗?

除非我的理解有误,否则这似乎是检查连接是否有效的无用时间,因为当应用程序尝试使用无效连接时会引发异常。

更新: 我问的真正问题是,应用程序中刚刚失败的陈旧连接是否仍会被检查回池中,最终一次又一次地失败?如果答案是否定的,那么签入测试将毫无意义,因为 'exploded' 连接永远不会被发送回池中。多年来,我一直在将 c3p0 与 testOnCheckIn 和测试查询间隔一起使用,但并未真正深入了解原因。

在结帐时测试连接是否是一个性能问题在很大程度上取决于测试的效率,以及连接在结帐时所做的工作量。正如您所说,配置连接测试的最安全、最简单的方法是在结帐时进行测试。但这也意味着客户必须体验测试的延迟。

如果您希望异步测试连接,即 客户端代码路径之外,那么您希望在签入时测试连接,并在连接签入时定期测试. 基本上,该策略是确保池中空闲的连接极不可能无效,这样您就可以安全地检出它们而无需测试。 ("Safely" 当然是程度问题。即使使用结帐时测试,连接也可能在测试和首次客户端使用之间失效。)

因为客户端刚刚使用过就假定连接在签入时有效不是一个好主意。首先,客户端遇到异常的事实并不一定会向池发出连接无效的信号。例如,对 Connection.commit() 的调用可能会因为事务中一行的并发修改而失败,即使 Connection 完全有效。从根本上说,签出连接时遇到的异常是客户的事,而不是池的事。 c3p0(我认为不寻常)确实注意到客户端遇到的异常,并触发静默连接测试,以便池可以推断连接有效性。但是有很多原因导致客户端可能会遇到来自有效连接的异常。

其次,池不能依赖于客户端最后一次使用连接后的快速签入。这是一个坏主意,但客户端通常会比实际的数据库工作时间更长地保持连接打开。 las 客户端使用和签入之间的任何 window 时间显然都是 window,在此期间连接可能会变坏。

因此,如果您想要异步连接测试——即(对于足够大的池)对客户端延迟没有贡献的连接测试——您需要进行签入测试,然后在连接被合并时进行频繁测试和闲置。

所有这一切都表明,实际上,异步连接测试的需求已经减少,因为 JDBC 驱动程序现在通过 Connection.isValid() 提供快速、可靠的测试。曾几何时,为了定义独立于 DBMS、可靠的连接测试,c3p0 必须默认对数据库元数据进行通常非常缓慢的查询。此测试的延迟可能会对客户端性能造成真正的影响。使用 Connection.isValid()(或高效的 preferredTestQuery),测试通常足够快,以至于结账测试的简单性和健壮性超过了小客户端延迟的影响。 c3p0 文档曾经建议出于性能原因使用异步连接测试。 current documentation 建议首先在结帐时进行同步测试,只有在测试延迟影响性能时才退回到异步测试作为潜在的优化。在大多数情况下,在实践中,您现在通常最终只使用 testConnectionsOnCheckout

Does this mean after the application has used the connection?

If so, wouldn't the connection always be valid if it was just used by the application?

如果您的应用使用了连接,但它似乎已断开,因此抛出了异常怎么办?在这种情况下,如果您只是将它放回池中,它将再次返回并且另一个请求将失败。一段时间后,更多的连接可能会中断,失败请求的比例会增加。

有些人倾向于在签入后进行测试的原因是他们害怕签出测试的性能影响。您的请求必须等到涉及网络流量的 SQL 查询完成。这可能会或不会对您的应用程序造成真正的危害。

我个人没有做过任何性能测试,也不知道这个问题有多严重,但这也取决于您对应用程序的延迟水平的期望。正如@Steve Waldman 所指出的那样,您也可能依赖于连接的异步测试 - 这样您就可以在请求处理线程之外测试您的连接。