2015-09-18 69 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