2015-09-18 106 views
12

几个月来,我的web应用程序运行平稳,但在过去的一两周内,它一直在断开与MySQL服务器的连接。我不是DBA的人,也不知道如何调试。MySQL/Hibernate - 我如何调试一直不断下降的MySQL池连接?

这是我所知道的:

  1. 连接似乎每隔几个小时下降。有时在白天,但总是在夜间。
  2. 我的实验室有一台MySQL服务器,它托管多个应用程序的数据库。
  3. 目前,我们有46个连接到MySQL服务器。
  4. 据我所知,没有其他应用程序遇到此问题。
  5. 我的应用程序使用同一个堆栈,配置,甚至代码连接到数据库的其他应用程序,该应用程序的其他支持每天大约有200个用户,2013年以来
  6. 已顺利运行这两个应用程序中使用Hibernate ORM;这是唯一的配置,我知道:

    <!-- TomcatJDBCConnectionProvider class is common to both applications --> 
    <property name="hibernate.connection.provider_class">org.hibernate.connection.TomcatJDBCConnectionProvider</property> 
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> 
    <property name="hibernate.connection.pool_size">5</property> 
    <property name="hibernate.current_session_context_class">thread</property> 
    <property name="hibernate.tomcatJdbcPool.validationQuery">SELECT 1</property> 
    <property name="hibernate.tomcatJdbcPool.testOnBorrow">true</property> 
    <property name="hibernate.enable_lazy_load_no_trans">true</property> 
    
  7. 问题开始大约在同一时间,当有人试图使用该应用程序的REST API下载我们的数据。这个用户 - 实际上是一个协作者 - 有一个小脚本迭代特定表中的每一行并请求所有元数据。

  8. 这个问题也出现在我的实验室开始提供Coursera Massive Open Online Course的同时。我不知道这些数字是什么,但网站上的实际使用量一定会增加。

我知道这是一个广泛的问题,但我真的很在意如何去调试这个问题。任何建议表示赞赏。

编辑:

周围的其他应用程序的ServletContextListener挖掘,我发现这段代码,我contextDestroyed功能没有:

// TODO: Find memory leak that requires server to be restarted after hot deploying several (3?) times. 
Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); 
for (Thread t : threadSet) { 
    if (t.getName().contains("Abandoned connection cleanup thread")) { 
     synchronized (t) { 
      System.out.println("Forcibly stopping thread to avoid memory leak: " + t.getName()); 
      t.stop(); // don't complain, it works 
     } 
    } 
} 

这似乎遍历堆栈跟踪,找到带文字"Abandoned connection cleanup thread"的手动停止。这似乎可能与我的问题有关?

编辑21/9/2015:

我的应用程序在本周末去了。下面是从昨天的错误日志中的堆栈跟踪(当我相信它去了):

20-Sep-2015 14:22:18.160 SEVERE [http-apr-8080-exec-35] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [edu.mssm.pharm.maayanlab.Harmonizome.api.GeneMetadataApi] in context with path [/Harmonizome] threw exception 
org.hibernate.exception.GenericJDBCException: Could not open connection 
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54) 
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125) 
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110) 
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:304) 
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.getConnection(LogicalConnectionImpl.java:169) 
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:67) 
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:160) 
    at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1395) 
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:224) 
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545) 
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124) 
    at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180) 
    at edu.mssm.pharm.maayanlab.Harmonizome.json.serdes.GeneMetadataSerializer.serialize(GeneMetadataSerializer.java:54) 
    at edu.mssm.pharm.maayanlab.Harmonizome.json.serdes.GeneMetadataSerializer.serialize(GeneMetadataSerializer.java:23) 
    at com.google.gson.TreeTypeAdapter.write(TreeTypeAdapter.java:70) 
    at com.google.gson.Gson.toJson(Gson.java:600) 
    at com.google.gson.Gson.toJson(Gson.java:579) 
    at com.google.gson.Gson.toJson(Gson.java:534) 
    at edu.mssm.pharm.maayanlab.Harmonizome.api.GeneMetadataApi.doGet(GeneMetadataApi.java:65) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) 
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) 
    at org.apache.tomcat.util.net.AprEndpoint$SocketWithOptionsProcessor.run(AprEndpoint.java:2440) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: org.apache.tomcat.jdbc.pool.PoolExhaustedException: [http-apr-8080-exec-35] Timeout: Pool empty. Unable to fetch a connection in 30 seconds, none available[size:5; busy:5; idle:0; lastwait:30000]. 
    at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:672) 
    at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:186) 
    at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:127) 
    at org.hibernate.connection.TomcatJDBCConnectionProvider.getConnection(TomcatJDBCConnectionProvider.java:208) 
    at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:292) 
    at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:297) 

这里是我的连接变量从MySQL:

mysql> SHOW VARIABLES LIKE '%connect%'; 
+-----------------------------------------------+-----------------+ 
| Variable_name         | Value   | 
+-----------------------------------------------+-----------------+ 
| character_set_connection      | utf8   | 
| collation_connection       | utf8_general_ci | 
| connect_timeout        | 5    | 
| default_master_connection      |     | 
| extra_max_connections       | 1    | 
| init_connect         |     | 
| max_connect_errors       | 100    | 
| max_connections        | 100    | 
| max_user_connections       | 0    | 
| performance_schema_session_connect_attrs_size | 512    | 
+-----------------------------------------------+-----------------+ 

mysql> SHOW VARIABLES LIKE '%timeout%'; 
+-----------------------------+----------+ 
| Variable_name    | Value | 
+-----------------------------+----------+ 
| connect_timeout    | 5  | 
| deadlock_timeout_long  | 50000000 | 
| deadlock_timeout_short  | 10000 | 
| delayed_insert_timeout  | 300  | 
| innodb_flush_log_at_timeout | 1  | 
| innodb_lock_wait_timeout | 50  | 
| innodb_rollback_on_timeout | OFF  | 
| interactive_timeout   | 28800 | 
| lock_wait_timeout   | 31536000 | 
| net_read_timeout   | 30  | 
| net_write_timeout   | 60  | 
| slave_net_timeout   | 3600  | 
| thread_pool_idle_timeout | 60  | 
| wait_timeout    | 28800 | 
+-----------------------------+----------+ 

编辑22/9/2015 :

将一个SEVERE Tomcat错误导致该问题?我看到一个错误,无关的数据库中,有关分析日期:

22-Sep-2015 10:09:53.481 SEVERE [http-apr-8080-exec-26] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [edu.mssm.pharm.maayanlab.Harmonizome.page.DatasetPage] in context with path [/Harmonizome] threw exception [javax.servlet.ServletException: javax.servlet.jsp.JspException: In &lt;parseDate&gt;, a parse locale can not be established] with root cause 
javax.servlet.jsp.JspException: In &lt;parseDate&gt;, a parse locale can not be established 
    at org.apache.taglibs.standard.tag.common.fmt.ParseDateSupport.doEndTag(ParseDateSupport.java:147) 

附加堆内存使用量的JConsole的输出:

enter image description here

JConsole的输出线程的使用;它开始大约24-25,一旦我开始使用该网站,跳到34。即使在关闭浏览器窗口后,它仍然存在:

enter image description here

编辑23/9/2015:

有一件事我改变权面前的问题开始了我如何处理Hibernate事务。以前,我禁用了​​(这是默认设置)。以前,我使用的是“open session in view”模式。好像人们不喜欢视图模式下的公开会话,所以我启用了​​。因此,我有这样的代码:

List<MyObjects> myObjects = null; 
try { 
    HibernateUtil.beginTransaction(); 
    myObjects = // fetch my objects from the DB 
    HibernateUtil.commitTransaction(); 
} catch (HibernateException he) { 
    HibernateUtil.rollbackTransaction(); 
} finally { 
    HibernateUtil.close(); 
} 

// render myObjects in JSP/JSTL 
// this JSP may lazily load related objects 

回想起来,这似乎...有问题。我不知道Hibernate何时“放开”对象。

+0

内存问题?任何其他应用程序部署在Mysql和Application部署的机器上? – Gaurav

+0

我对这个连接池实现没有经验,但我会开始调试实现QueryTimeoutInterceptor:https://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html#org.apache.tomcat。 jdbc.pool.interceptor.QueryTimeoutInterceptor – a1ex07

+0

@Gaurav,其他一些小应用程序在同一台机器上运行。我们有一个集群,出于历史原因,我每次都在同一台计算机上运行应用程序,但我已经重新部署,因此它可以在任何地方运行,以防一台计算机出现问题。 – gwg

回答

4

从你提供的堆栈跟踪中,我可以得出一个结论:你只是没有连接。

这可能是由长时间运行事务造成的,可能是由于查询速度慢或应用程序事务边界不正确。

我建议你开始使用FlexyPool,它支持Tomcat DBCP,并更好地理解连接和事务使用情况。 This article解释了您可能感兴趣的直方图,例如连接获取时间和连接租用时间。

为了安全起见,请检查MySQL驱动程序版本,看看您是否运行在过时的库上。

+0

我会检查FlexyPool和那篇文章。请在23/9/2015看看我的编辑,并让我知道如果我的对象延迟加载似乎有问题。 – gwg

+0

使用“在视图中打开会话”并不是一个好主意,因为事务必须等待响应发送到客户端才能提交。交易时间越长,对连接池的压力就越大,因为连接将在较长的时间间隔内租用。 –

+0

这可能是一个坏主意,我可以在将来寻找替代品,但是我删除了'enable_lazy_load_no_trans'并以查看模式重新实现了打开的会话。现在,我的应用程序已运行5天而没有数据库连接超时。我相当清楚,我们很清楚。我肯定会研究FlexyPool,并且该文章看起来很有用 - 如果您有一个关于在视图中打开会话的干净替代方案的建议,我很乐意听到它。 – gwg

4

Hibernate错误有点抽象,有时候通过堆栈跟踪发现错误可能会非常棘手。我认为这可能是您的应用程序的问题,也许您在某些情况下未正确关闭Hibernate连接,或者您的应用程序可能存在内存泄漏。

您是否试图通过JDK的jconsole监控应用程序?

您可以设置这个在Java参数你的Tomcat配置控制台(我假设你正在使用Tomcat)上,以使jconsole

-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=8086 
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false 

然后连接到远程过程为例

localhost:8086 

并在通过使应用程序停止的操作时观察线程。

编辑

如果你不使用Tomcat和你在Windows环境下运行的应用程序,你可以使用例如Process Explorer monitorize线程和monitorize您的应用程序。

+0

哇。我从来没有见过这个。在OS X上使用Eclipse,我将这些参数放在'Open launch configuration> Arguments'中。该端口应该用于我的应用程序? – gwg

+0

我已经添加了内存使用情况的截图。 – gwg

+0

您必须设置一个未被使用的端口才能与jconsole连接。但似乎你已经设法连接:) 你可以把线程而不是内存的打印?打印一些线索,比做一些你认为有问题的随机动作,然后再打印一次。并共享 – RageAgainstTheMachine

2

看起来您的连接池不能在超时期限内返回到Hibernate的免费连接。发生这种情况是因为您的应用程序有很长的事务或事务死锁。您可以尝试以下选项来修复该错误。

  1. 变化,下面一行

    <property name="hibernate.connection.pool_size">5</property>

连接池的大小使池大小约10和测试。您应该注意与数据库连接的数量。如果超过mysql数据库连接限制,更改mysql服务器的max_connections并继续测试。

  1. 使用另一个连接池。我推荐使用apache commons dbcp2。如下所示的dbcp2的Maven依赖关系。

    <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.1</version> </dependency>

添加dbcp2到您的POM然后配置dbcp2与您的应用程序。

如果这是解决方案,您的应用程序只有很长的交易。有时它可能会最小化事件发生,如果它仍然发生,您的应用程序肯定会发生事务死锁。所以你必须确定你的代码有什么可能的问题。

还有其他替代解决方案,例如将等待超时更改为更高值。但这对您的应用程序性能不利,对于事务死锁没有任何意义。最后,你应该记住在进一步的开发中关心事务管理和数据库结构以获得更好的数据库性能。

+0

你正在测试一个更大的连接池吗?现在发生了什么? –

+0

我不想重启,因为我删除了'enable_lazy_load_no_trans',所以我的服务器运行平稳。看到我的评论弗拉德。 – gwg