连接池是什么鬼东西

Posted 背井

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了连接池是什么鬼东西相关的知识,希望对你有一定的参考价值。


我们来谈谈连接池。


我甚至可以这么说:

大多数常用连接池的默认配置都很差!


对你来说,这意味着:

你得赶紧去检查你的连接池配置。


如果依赖默认设置,你的应用可能会出现问题。它可能导致内存泄漏和程序失去响应(即使负载并不高)。


下面我将展示一些最重要的设置和我的如何配置它们的建议。


WHAT IS CONNECTION POOL? 什么是连接池?


一个需要从数据库中写入或读取数据的普通web应用程序,它会有如下行为:


  1. 打开数据库连接  //消耗N毫秒

  2. 读/写数据

  3. 关闭连接


(顺便说一下,在老旧的CGI应用程序中,这是唯一可行的方法)


这种方式在很多情况下都表现良好。你也不需要别的什么了。但对于高性能系统来说,它有一些缺点:


  • 第一步需要消耗一些时间。可能是几十毫秒或几百毫秒(当然,这取决于具体情况)。

  • 步骤3(关闭连接)很容易被遗漏,这会导致连接泄漏(导致内存泄漏和其他问题)。


A NEW HERO 超人登场


这就是另一种方法诞生的原因:应用程序可以预先打开一堆连接,并一直保持着它们的开启状态。这些打开的连接就组成了连接池。那么任何操作都将变成这样的:


  1. 从池子中取出一个数据库连接  // 大部分情况下这一步都是飞快的

  2. 读/写数据

  3. 将连接返还到池子中


看起来很酷。但新的力量总是伴随着新的问题。


… AND NEW PROBLEMS 新问题来了


使用连接池时,我们需要(至少)解决以下问题


  • 我们应该保持多少连接?

  • 它们应该保持多久?

  • 如果它们看起来坏掉了呢?

  • 如果应用程序需要的连接数超过池子当前所持有的数目怎么办?

  • 如果有人忘记将连接返还给池子怎么办?


为了应对这些问题,连接池提供了很多设置。不过它们的默认值大多是惨不忍睹的。有兴趣吗?让我展示一下。


BASIC SETTINGS 基础设置


我将考虑Java世界中最流行的两个连接池实现:


  • C3P0 (https://www.mchange.com/projects/c3p0/)

  • HikariCP (https://github.com/brettwooldridge/HikariCP)


基础的参数当然是:


  • min size (最小规格,任何时刻所开启的最小连接数)

  • initial size (初始规格,应用启动时打开的连接数)

  • max size (最大规格,连接池中所能保留的最大连接数)


顺便说一下,这些是唯一具有合理默认值的设置。它们分别是:



c3p0 HikariCP
min size 3 10
initial size 3 10
max size 15 10


我们接下来看看更多有问题的设置。


CRITICAL SETTINGS 关键设置


CHECKOUT TIMEOUT 检出超时


应用程序从池子获取连接之前可以等待多长时间。


  • c3p0 设置项: checkoutTimeout

  • HikariCP 设置项: connectionTimeout


c3p0 HikariCP I recommend
checkoutTimeout 30 s 1 ms


Both default values are just disaster.  两边的默认值都是灾难性的。


正如之前所提到的,在大多数情况下,从池子获得连接的速度非常快。除非池子中没有多余的打开的连接。这时池子需要获取一个新连接(通常用不了一秒钟)。但是,如果达到maxSize,池子将无法打开新连接,只能等待有人将其连接返还。但是,如果应用程序有连接泄漏(一个忘了返回连接的bug),池子将永远无法回收放出去的连接!


What then happens? 然后会发生什么?


在c3p0的情况下,所有线程都被冻结在以下状态:


"qtp1905485420-495 13e09-3211" #495 prio=5 os_prio=0 tid=0x00007f20e078d800 nid=0x10d7 in Object.wait() [0x00007f204bc79000]

  java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable()

- locked <0x00000000c3295ef8> (a com.mchange.v2.resourcepool.BasicResourcePool)

at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource()

   …

   at org.hibernate.jpa.internal.QueryImpl.getResultList()

   at domain.funds.FundsRepository.get()

   …


看起来HikariCP的默认值“30秒”要好一点。其实不然,它在高性能应用程序中并没有真正的帮助。在这30秒内,可能会有很多新的请求,而所有请求都被冻结了。显然,应用程序将很快出现内存错误(OutOfMemory)。任何等待(waiting)只是让程序在死亡前多存活了几秒。


这就是为什么我建议将 checkoutTimeout 设置为可能的最小值:1ms。不幸的是,我们不能将其设置为0,因为0意味着无休止的等待。我们越早失败,我们就越有可能给工作线程完成其工作的机会。我们可以清楚地通知用户应用程序当前已超载,他应该稍后再试。


TEST CONNECTION ON CHECKOUT 检出时连接检查


有时池子中的连接可能会断开。数据库可能主动关闭了它们,或者系统管理员动了网线。这也是池子需要监视连接有效性的原因。


最简单的设置是 c3p0 中的 “testConnectionOnCheckout”(我在HikariCP中还没有找到类似的设置,它似乎总是启用的)。


默认值:


c3p0 HikariCP I recommend
testConnectionOnCheckout false true? true


Yes, 它默认就应该是 启用的 !


否则,日志中会出现很多这样的异常:


org.hibernate.TransactionException: Unable to rollback against JDBC Connection
at o.h.r.j.i.AbstractLogicalConnectionImplementor.rollback()
at o.h.r.t.b.j.i.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.rollback(JdbcResourceLocalTransactionCoordinatorImpl.java:294)


P.S. 如果你想获得更好的性能,可以考虑在后台进行连接测试,而不是在检出时:


  • testConnectionOnCheckout=false

  • testConnectionOnCheckin=true

  • idleConnectionTestPeriod=10


PREFERRED TEST QUERY 偏好的检测语句


但池子应该如何测试连接呢?


问题是,它取决于数据库。


默认情况下,两类池子都通过执行:


  • connection.isValid()” (在使用 JDBC4 的情况下)

  • connection.getMetaData().getTables()” (在 JDBC3 的情况下)


它可能很慢,因为 “getTables()” 每次都检索有关所有表的元信息。我们推荐如下值:


  • SELECT 1” (对于 mysql)

  • SELECT 1 FROM DUAL” (对于 Oracle) 


通过执行这个简单而快速的查询,池子可以检查连接是否仍然存活。


MAX IDLE TIME 最大闲置时间


未使用的连接可以在池中保留多长时间:


默认值是:


c3p0 HikariCP I recommend
maxIdleTimeout 10 minutes 1..10 minutes


看着会觉得没什么大不了的,但每个打开的连接:


  • 会在数据库中占用一些资源

  • 会阻止其他系统连接到同一个数据库(每个数据库都有最大连接数的限制)


这就是为什么关闭未使用(空闲)连接是个好主意。我建议不要将此值设置为无限大。也许短短几分钟较为合理。


MIN POOL SIZE 最小连接数


池子中至始至终应该至少有多少个连接(即使未使用)。


  • c3p0 设置项: minPoolSize

  • HikariCP 设置项: minimumIdle


默认值:



c3p0 HikariCP I recommend
maxIdleTimeout 3 max pool size 0…N


出于同样的原因,关闭未使用的连接可能是个好主意。在大多数情况下,我会将此值设置为0或1。如果某个用户意外地决定在半夜登录到您的应用程序,他只需再等待几毫秒。不会有什么影响。


MAX CONNECTION AGE 连接最大存活时间


连接可以在池中存在多长时间(无论是空闲还是使用过)


  • c3p0 设置项: maxConnectionAge

  • HikariCP 设置项: maxLifetime


默认值:



c3p0 HikariCP I recommend
maxIdleTimeout 30 minutes say, 30 minutes


以防万一,时不时地关闭连接可能是个好主意。这很可能有助于避免一些内存泄漏。


HikariCP 文档中有如下表述:


“We strongly recommend setting this value, and it should be several seconds shorter than any database or infrastructure imposed connection time limit.”


UNRETURNED CONNECTION TIMEOUT 未返还连接的超时


一个典型的问题是连接泄漏。一些错误代码从池中获取连接,但没有返还。如何发现这个问题?


幸运的是,也存在针对这类情况的设置项:


  • c3p0 设置项: unreturnedConnectionTimeout

  • HikariCP 设置项: leakDetectionThreshold


默认值:



c3p0 HikariCP I recommend
maxIdleTimeout disabled disabled 5 minutes?


如果有任何错误代码建立了连接,但在5分钟内未返还,则池子将强制返回连接并发出如下警告:


[C3P0PooledConnectionPoolManager Logging the stack trace by which the overdue resource was checked-out.
java.lang.Exception: DEBUG STACK TRACE: Overdue resource check-out stack trace.
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource()
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1885)
at domain.application.ApplicationReportSender.sendWeeklyReport(ApplicationReportSender.java:63)


它有助你定位问题代码。


CONCLUSION 总结


我概述了一些连接池设置。其实还有更多。我根据自己的经验提出了一些合理的建议。但是你的应用程序可能有不同的负载。你的用户可能有不同的行为。你也可能会觉得我的建议很愚蠢。


没关系。不必全信我,但也请:


Don’t trust defaults.  不要轻信默认配置。


去检查下你的连接池配置吧!




本文译自:https://www.javaadvent.com/2018/12/wtf-connection-pools.html

原文标题:WTF CONNECTION POOLS

作者的网站上有很多优秀文章,感兴趣的读者可以点 阅读原文 查看。





关注作者

连接池是什么鬼东西

欢迎关注,一起学习 还请多多转发分享



以上是关于连接池是什么鬼东西的主要内容,如果未能解决你的问题,请参考以下文章

讲讲 Python Launcher 是什么鬼东西?

讲讲 Python Launcher 是什么鬼东西?

CTF---Web入门第一题 what a fuck!这是什么鬼东西?

PostCSS 是个什么鬼东西?

数据库连接池是啥

web-what a fuck!这是什么鬼东西?