#java #mysql #hibernate #spring #transactions
#java #mysql #переход в спящий режим #spring #транзакции
Вопрос:
Основная проблема, которую я хочу решить, заключается в выполнении задачи, которая генерирует несколько временных таблиц в MySQL, которые должны оставаться достаточно долго, чтобы получать результаты из Java после их создания. Из-за размера задействованных данных задача должна выполняться пакетно. Каждый пакет представляет собой вызов хранимой процедуры, вызываемой через JDBC. Весь процесс может занять полчаса или больше для большого набора данных.
Чтобы обеспечить доступ к временным таблицам, я запускаю всю задачу, от начала до конца, в одной транзакции Spring с TransactionCallbackWithoutResult. В противном случае я мог бы получить другое соединение, у которого нет доступа к временным таблицам (это иногда случалось до того, как я оборачивал все в транзакцию).
Это отлично работало в моей среде разработки. Однако в процессе производства я получил следующее исключение:
java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
Это произошло, когда другая задача попыталась получить доступ к некоторым из тех же таблиц во время выполнения моей длительной транзакции. Что меня смущает, так это то, что длительно выполняющаяся транзакция только вставляет или обновляет временные таблицы. Весь доступ к невременным таблицам предоставляется только selects. Из той документации, которую я могу найти, уровень изоляции транзакции Spring по умолчанию не должен приводить к блокировке MySQL в этом случае.
Итак, мой первый вопрос: правильный ли это подход? Могу ли я гарантировать, что я неоднократно получаю одно и то же соединение через шаблон Hibernate без длительной транзакции?
Если подход к длительным транзакциям правильный, что я должен проверить с точки зрения уровней изоляции? Правильно ли я понимаю, что уровень изоляции по умолчанию в транзакциях Spring / MySQL не должен блокировать таблицы, доступ к которым осуществляется только через selects? Что я могу сделать, чтобы отладить, какие таблицы вызывают конфликт, и предотвратить блокировку этих таблиц транзакцией?
Ответ №1:
Я считаю, что держать транзакцию открытой в течение длительного времени — зло. За время моей карьеры определение «расширенный» снизилось с секунд до миллисекунд.
Это нескончаемый источник неповторяемых проблем и головокружительных проблем.
В этом случае я бы стиснул зубы и сохранил «рабочий журнал» в программном обеспечении, который вы можете воспроизвести в обратном порядке, чтобы очистить, если пакет завершится неудачей.
Комментарии:
1. Есть ли у вас решение проблемы «поддерживать соединение»? В моем случае Spring и Hibernate обеспечивают мое соединение с базой данных, и у меня были проблемы с получением одного и того же соединения каждый раз, не прибегая к объединению всего в одну транзакцию. Без поддержания того же соединения я теряю доступ к временным таблицам.
2. Если вы ведете рабочий журнал с идентификатором и контрольными точками, достигнутыми при пакетной обработке, вы можете создавать обычные таблицы с именами типа TEMP_<batchid>_STUFF. На основе достигнутого рубежа вы можете пропустить вперед и продолжить прерванные пакетные процессы. После завершения обработки (т. Е. завершения достигнутой вехи) удалите таблицы и зарегистрируйте, что достигнутая веха «ОЧИЩЕНА». Теперь вся обработка может выполняться с помощью мелкозернистых транзакций. Неудобство такого подхода заключается в том, что вы не видите «моментальный снимок» состояния базы данных при запуске пакетного задания. Это может быть, а может и не быть проблемой.
3. Это очень хороший совет. Однако у меня есть одна оставшаяся проблема. Часть начальной обработки создает временные таблицы, доступ к которым осуществляется в каждом пакете. Эта начальная обработка занимает значительное количество времени, достаточное для того, чтобы значительно снизить производительность, если бы ее приходилось повторять для каждого пакета. Я не уверен, как справиться с этой начальной обработкой, но я еще немного подумаю.
Ответ №2:
Когда вы говорите, что ваша таблица временная, ограничена ли она областью транзакции? Это может привести к тому, что другие транзакции (возможно, в другой транзакции) не смогут ее увидеть / получить к ней доступ. Возможно, объединение, включающее реальную таблицу и временную таблицу, каким-то образом блокирует реальную таблицу.
Основная причина: Пытались ли вы использовать инструменты MySQL, чтобы определить, что блокирует соединение? Это может быть что-то вроде блокировки следующей строки. Я не очень хорошо знаю инструменты MySQL, но в Oracle вы можете видеть, какие соединения блокируют другие соединения.
Время ожидания транзакции:Вам следует создать второй пул подключений / источник данных с гораздо более длительным таймаутом. Используйте этот пул соединений для вашей длительной задачи. Я думаю, что ваша производственная среда «пытается» помочь вам, обнаруживая зависшие соединения.
Ответ №3:
Как упоминал Джастин относительно времени ожидания транзакции, я недавно столкнулся с проблемой, при которой пул соединений (в моем случае tomcat dbcp в Tomcat 7) имел настройку, которая должна была отмечать длительные соединения пометкой abandon, а затем закрывать их. После настройки этих параметров я смог избежать этой проблемы.