android wtf
让我们谈谈连接池。
我声称:
大多数流行的连接池的默认设置都很差!
对您来说,这意味着:
去查看您的连接池设置。
如果您依赖默认设置,则可能会遇到问题。 您可能有内存泄漏和应用程序无响应(即使负载根本不高)。
下面,我将显示一些最重要的设置以及我的建议,这些设置应如何配置。
什么是连接池?
一个普通的Web应用程序需要从数据库写入或读取数据,它是这样的:
- 打开与数据库的连接//需要N毫秒
- 读/写数据
- 关闭连接
(顺便说一下,在旧的良好CGI应用程序中,这是唯一可能的方法)
在许多情况下,此方法非常好。 而且您可能不需要更多。 但这对于高性能系统有一些缺点 :
- 步骤1可能需要一些时间。 大概几十或几百毫秒(当然要取决于它)。
- 很容易忘记第3步(关闭连接),这会导致连接泄漏 (导致内存泄漏和其他问题)。
新英雄
这就是诞生另一种方法的原因:应用程序可能会预先打开一堆连接并一直保持打开状态。 一堆打开的连接称为连接池 。 然后任何操作如下所示:
- 在大多数情况下,从池中//快速建立数据库连接
- 读/写数据
- 将连接返回到池
看起来很酷。 但是,新的力量总是意味着新的问题。
…以及新问题
使用连接池时,我们需要(至少)解决以下问题 :
- 我们应该保持多少连接?
- 应该保留多长时间?
- 如果它们看起来坏了怎么办?
- 如果应用程序需要的连接数超过当前池的数量,该怎么办?
- 如果有人忘记将连接恢复到池怎么办?
为了回答这些问题,连接池有很多设置。 而且它们的默认值大多不好。 感兴趣吗? 让我展示。
基本设置
我将考虑Java世界中2个最流行的连接池:
- C3P0( https://www.mchange.com/projects/c3p0/ )
- HikariCP( https://github.com/brettwooldridge/HikariCP )
原因的基本参数是:
- 最小大小(随时应打开的最小连接数)
- 初始大小(启动时打开了多少个连接应用程序)
- 最大大小(池中的最大连接数)
顺便说一下,这些是唯一具有合理默认值的设置。 他们来了:
c3p0 | 光ikaCP | |
最小尺寸 | 3 | 10 |
初始尺寸 | 3 | 10 |
最大尺寸 | 15 | 10 |
让我们继续进行更多有问题的设置。
关键设置
结帐超时
应用程序可以等待多长时间,直到它从池中获得连接。
- c3p0设置:checkoutTimeout
- HikariCP设置:connectionTimeout
默认值:
c3p0 | 光ikaCP | 我建议 | |
checkoutTimeout | ∞ | 30秒 | 1毫秒 |
这两个默认值都只是灾难。
正如我提到的,在大多数情况下,从池中获得连接非常快。 除非池中没有打开的连接。 然后,池需要获取一个新的连接(通常需要不到一秒钟的时间)。 但是,如果达到maxSize,则池无法打开新的连接,而只是等到有人将其连接返回到池中。 但是,如果应用程序发生连接泄漏(阻止连接返回的错误),则池将永远无法恢复连接!
那会发生什么呢?
对于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错误。 任何等待只会将应用程序的终止延迟几秒钟。
这就是为什么我建议将checkoutTimeout设置为最小可能值:1ms。 不幸的是,我们不能将其设置为0,因为0意味着无尽的等待&#55357;&#56898; 我们越早失败,我们给工作线程完成工作的机会就越大。 而且我们可以清楚地通知用户该应用程序当前已超载,他应该稍后再试。
在结帐时测试连接
有时池中的连接可能会死。 数据库可以主动关闭它们,或者系统管理员可以断开网络电缆。 这就是池应该监视连接活动性的原因。
最简单的设置是c3p0中的“ testConnectionOnCheckout”(我在HikariCP中没有找到类似的设置,它似乎始终处于启用状态)。
默认值:
c3p0 | 光ikaCP | 我建议 | |
testConnectionOnCheckout | 假 | 真正? | 真正 |
当然,默认情况下应该启用它 !
否则,您将在日志中最终遇到许多此类异常:
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)
PS如果要获得更好的性能,可以考虑在后台而不是在结帐时测试连接:
- testConnectionOnCheckout = false
- testConnectionOnCheckin = true
- idleConnectionTestPeriod = 10
首选测试查询
但是池应该如何准确地测试连接?
问题在于它取决于数据库。
默认情况下,两个池都通过执行以下测试来测试连接
- “ connection.isValid()”(对于JDBC4),或者
- “ connection.getMetaData()。getTables()”(对于JDBC3)
这可能很慢,因为“ getTables()”每次都检索有关所有表的元信息。 推荐值类似于
- “ SELECT 1”(对于MySql),或者
- “从双重选择1”(对于Oracle)等
通过执行此简单而快速的查询,池可以检查连接是否仍处于活动状态。
最大空闲时间
未使用的连接可以在池中保留多长时间
- c3p0设置:maxIdleTime
- HikariCP设置:idleTimeout
默认值:
c3p0 | 光ikaCP | 我建议 | |
maxIdleTimeout | ∞ | 10分钟 | 1..10分钟 |
可能没什么大不了的,但是每个打开的连接
- 在数据库中保存一些资源
- 防止其他系统获得到同一数据库的连接(每个数据库都有最大可能连接数的限制)
因此,关闭未使用的(空闲)连接是个好主意。 我建议将此值设置为无限期。 大概几分钟是合理的。
最小泳池大小
应始终具有多少个连接池(即使未使用)。
- c3p0设置:minPoolSize
- HikariCP设置:minimumIdle
默认值:
c3p0 | 光ikaCP | 我建议 | |
maxIdleTimeout | 3 | 最大游泳池 | 0…N |
出于同样的原因,关闭未使用的连接可能是一个好主意。 在大多数情况下,我会将此值设置为0或1。 如果某些用户意外决定在午夜登录到您的应用程序,那么他将只等待几毫秒。 没什么大不了的。
最大连接年龄
连接在池中可以存在多长时间(无论它是空闲还是已使用)
- c3p0设置:maxConnectionAge
- HikariCP设置:maxLifetime
默认值:
c3p0 | 光ikaCP | 我建议 | |
maxIdleTimeout | ∞ | 30分钟 | 比方说30分钟 |
以防万一,不时关闭连接可能是一个好主意。 可能有助于避免某些内存泄漏。
来自HikariCP文档的报价:
“我们强烈建议设置此值,它应该比任何数据库或基础架构施加的连接时间限制短几秒钟。”
未返回的连接超时
典型的问题之一是连接泄漏。 一些错误的代码从池中获取了一个连接,但没有返回它。 如何发现这个问题?
幸运的是,在这种情况下,我们有一个很好的设置:
- c3p0设置:unreturnedConnectionTimeout
- HikariCP设置:leakDetectionThreshold
默认值:
c3p0 | 光ikaCP | 我建议 | |
maxIdleTimeout | 残障人士 | 残障人士 | 5分钟? |
如果任何错误代码接受了连接但在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)
这将帮助您找出有罪代码在哪里。
结论
我概述了一些连接池设置。 还有更多。 根据我的经验,我给出了一些合理的建议。 但是您的应用程序可能具有不同的负载。 您的用户可能会有不同的行为。 我的建议对您来说似乎很愚蠢。
没问题。 不要相信我 但也请不要相信默认值。
去检查你的游泳池设置!
翻译自: https://www.javacodegeeks.com/2018/12/wtf-connection-pools.html
android wtf