Подтвердить что ты не робот

MySQL/Hibernate - Как отлаживать соединение с пулом MySQL, которое продолжает снижаться?

В течение нескольких месяцев мое веб-приложение выполнялось гладко, но на прошлой неделе или два он продолжает отказываться от подключения к серверу MySQL. Я не парень DBA и не знаю, как отладить это.

Вот что я знаю:

  • Соединение, кажется, падает каждые несколько часов. Иногда в течение дня, но всегда в течение ночи.
  • В моей лаборатории есть сервер MySQL, на котором размещаются базы данных для нескольких приложений.
  • В настоящее время у нас есть 46 подключений к серверу MySQL.
  • Насколько мне известно, никакое другое приложение не сталкивается с этой проблемой.
  • Мое приложение использует тот же стек, конфигурацию и даже код для подключения к БД в качестве другого приложения - это другое приложение поддерживает около 200 пользователей в день и работает плавно с 2013 года.
  • Оба приложения используют 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>
    
  • Проблема началась примерно в то же время, когда кто-то пытался использовать API RESTful для загрузки наших данных. Этот пользователь, фактически являющийся соавтором, имеет небольшую script итерацию по каждой строке в конкретной таблице и запрашивает все метаданные.

  • Проблема также началась примерно в то же время, когда моя лаборатория начала предлагать курс Coursera Massive Open Online. Я не знаю, что такое цифры, но фактическое использование на сайте должно было подскочить.

Я знаю, что это широкий вопрос, но я действительно не понимаю, как это сделать. Любые предложения приветствуются.

EDIT:

Копаясь вокруг другого приложения 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" и вручную остановите его. Вероятно, это связано с моей проблемой?

EDIT 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 использования памяти кучи:

введите описание изображения здесь

Выход JConsole для использования потоков; он начался около 24-25 и подскочил до 34, как только я начал использовать сайт. Даже после закрытия окна браузера он остался там:

введите описание изображения здесь

EDIT 23/9/2015:

Одна вещь, которую я изменил прямо перед тем, как возникла проблема, - это то, как я занимаюсь транзакциями Hibernate. Раньше у меня был enable_lazy_load_no_trans отключен (по умолчанию). Раньше я использовал шаблон open session in view. Казалось, что людям не понравился открытый сеанс в виде, поэтому я включил enable_lazy_load_no_trans. Таким образом, у меня есть такой код:

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 "отпускает" объекты.

4b9b3361

Ответ 1

Из предоставленной вами трассировки стека я могу сделать один вывод: у вас просто закончились соединения.

Это может быть вызвано длительными транзакциями, возможно, из-за медленных запросов или неправильных границ транзакций приложения.

Я предлагаю вам начать использовать FlexyPool, который поддерживает Tomcat DBCP и лучше понимает как подключение, так и использование транзакции. В этой статье объясняются гистограммы, которые могут вас заинтересовать, например время получения связи и время аренды.

Чтобы быть в безопасности, проверьте версию драйвера MySQL и посмотрите, работает ли вы в устаревшей библиотеке.

Ответ 2

Ошибки спящего режима немного абстрактны, и иногда бывает сложно найти ошибку по трассировке стека. Я думаю, что это может быть проблема вашего приложения, возможно, вы не закрываете соединения Hibernate должным образом в некоторых случаях, или ваше приложение может иметь утечку памяти.

Вы пытались контролировать приложение с помощью jconsole из JDK?

Вы можете установить это на своей консоли конфигурации Tomcat в аргументах Java (я предполагаю, что вы используете 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 

и следить за потоками, пока вы выполняете операции, которые заставляют приложение останавливаться.

Edit

Если вы не используете Tomcat и запускаете приложение в среде Windows, вы можете отслеживать потоки, используя, например, Process Explorer и отслеживать ваше приложение.

Ответ 3

Кажется, ваш пул соединений не может вернуть бесплатное соединение с Hibernate в течение времени ожидания. Это происходит потому, что ваше приложение имеет очень длинные транзакции или транзакционные блокировки. Вы можете попробовать следующие варианты исправления ошибки.

  • измените размер пула подключений в следующей строке

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

сделать размер пула около 10 и проверить. Вы должны следить за количеством подключений к вашей базе данных. Если он превышает ограничения подключения к базе данных mysql, измените max_connections сервера mysql и продолжайте тестирование.

  1. Использовать другой пул соединений. Я рекомендую использовать apache commons dbcp2. Maven зависимости dbcp2 следующим образом.

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

Добавьте dbcp2 в свой POM, а затем config dbcp2 с вашим приложением.

Если это было решение, у вашего приложения были только длинные транзакции. Иногда это может свести к минимуму возникновение, и, если это все еще происходит, определенно ваше приложение имеет блокировку транзакций. Таким образом, вы должны определить, какие проблемы могут возникнуть с вашим кодом.

Существуют и другие альтернативные решения, которые меняют время ожидания ожидания на большее значение. Но это не очень хорошо для производительности вашего приложения, и это не имеет никакого смысла для транзакционных блокировок. Наконец, вы должны помнить об управлении транзакциями и структуре базы данных в своих дальнейших разработках для лучшей производительности базы данных.