Hibernate: тупик найден при попытке получить блокировку
Я использую hibernate в своем проекте, и я получаю случайные очевидные тупики для очень простых операций с базой данных.
Существует один из следов стека: https://gist.github.com/knyttl/8999006 - что меня смущает, так это то, что первое исключение-это RollbackException,а затем есть исключения LockAquisition.
Проблема часто возникает на подобных предложениях:
@Transactional
public void setLastActivity() {
User user = em.findById(...);
user.setLastActivity(new Date());
em.merge(user);
em.flush();
}
Я совершенно застрял, так как не знаю, является ли это проблемой Hibernate, MySQL или C3P0.
Моя конфигурация гибернации:
<prop key="hibernate.dialect">${database.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">${database.structure}</prop>
<prop key="hibernate.connection.url">${database.connection}</prop>
<prop key="hibernate.connection.username">${database.username}</prop>
<prop key="hibernate.connection.password">${database.password}</prop>
<prop key="hibernate.connection.driver_class">${database.driver}</prop>
<prop key="hibernate.connection.shutdown">true</prop>
<prop key="hibernate.connection.writedelay">0</prop>
<prop key="hibernate.connection.characterEncoding">UTF-8</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
<prop key="hibernate.show_sql">${database.show_sql}</prop>
<prop key="hibernate.format_sql">false</prop>
<prop key="hibernate.ejb.metamodel.generation">disabled</prop>
<!-- Use the C3P0 connection pool provider -->
<prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
<prop key="hibernate.c3p0.min_size">0</prop>
<prop key="hibernate.c3p0.max_size">50</prop>
<prop key="hibernate.c3p0.timeout">120</prop>
<prop key="hibernate.c3p0.max_statements">0</prop>
<prop key="hibernate.c3p0.max_statementsPerConnection">0</prop>
<prop key="hibernate.c3p0.maxStatementsPerConnection">0</prop>
<prop key="hibernate.c3p0.idle_test_period">120</prop>
<prop key="hibernate.c3p0.acquire_increment">1</prop>
<prop key="hibernate.c3p0.numHelperThreads">8</prop>
EDIT1:
- я писал выше, что происходили очевидные тупики-это было неправильно,только" тупик, найденный при попытке получить блокировку".
EDIT2:
Это происходит также на этих методах - те должны быть аннотированы с помощью @Transactional:
@Transactional
public void setLastActivity() {
em.insertNative("table")
.values(...)
.execute();
}
2 ответа:
Поскольку взаимоблокировки происходят так часто, похоже, что некоторые потоки приложения удерживают блокировки в течение длительного периода времени.
Каждый поток в приложении будет использовать свое собственное соединение/соединения с базой данных при доступе к базе данных, поэтому с точки зрения базы данных два потока-это два разных клиента, которые конкурируют за блокировки базы данных.
Если поток держит замки в течение длительного периода времени и получает их в определенном порядке, и второй поток приходит, приобретая те же самые блокировки, но в другом порядке, взаимоблокировка обязательно произойдет (смотрите здесь для подробностей об этой частой причине взаимоблокировки).
Также взаимоблокировки происходят в операциях чтения, что означает, что некоторые потоки также получают блокировки чтения. Это происходит, если потоки выполняют транзакции на уровне изоляции
REPEATABLE_READ
илиSERIALIZABLE
.Чтобы решить эту проблему, попробуйте найти использование
Isolation.REPEATABLE_READ
иIsolation.SERIALIZABLE
в проекте, чтобы увидеть, является ли это быть используемым.В качестве альтернативы используйте уровень изоляции по умолчанию
READ_COMMITTED
и аннотируйте сущности с помощью@Version
, чтобы обрабатывать параллелизм с помощью оптимистической блокировки.Также попробуйте определить длительные транзакции, это иногда происходит, когда
@Transactional
помещается в неправильном месте и обертывает, например, обработку целого файла в Примере пакетной обработки, вместо того, чтобы делать транзакции строка за строкой.Это конфигурация log4j для регистрации создание / удаление менеджеров сущностей и транзакций begin / commit/rollback:
<!-- spring entity manager and transactions --> <logger name="org.springframework.orm.jpa" additivity ="false"> <level value="debug" /> <appender-ref ref="ConsoleAppender" /> </logger > <logger name="org.springframework.transaction" additivity ="false"> <level value="debug" /> <appender-ref ref="ConsoleAppender" /> </logger >
- могу ли я каким-то образом выполнить запрос обновления (либо JPA/Native) без необходимости блокировки таблицы через @Transactional?
Запросы обновления возможны через собственные запросы илиJPQL .
- могу ли я как-то войти в сеанс без использования @Transactional? например, запланированный поток пытается прочитать ленивое поле на сущности уступает LazyInitializationException-нет сеанса, если метод не аннотирован @Transactional
В методах без
@Transactional
запросы будут выполняться в собственном менеджере сущностей и возвращать только обособленные сущности, так как сеанс закрывается сразу после выполнения запроса.Таким образом, исключения ленивой инициализации в методах без
@Transactional
являются нормальными. Вы также можете установить их в@Transactional(readOnly=true)
.
Это ошибка с MySQL.
Самый простой способ разрешить и избежать тупиковых ситуаций-это изменить порядок операций БД, происходящих в приложении.
Взаимоблокировка в основном происходит, когда несколько ресурсов / соединений пытаются получить более одной блокировки в противоположных порядках, как показано ниже:
connection 1: locks key(1), locks key(2); connection 2: locks key(2), locks key(1);
В сценарии, когда оба соединения выполняются одновременно, соединение 1 получит блокировку по ключу (1), а соединение 2-по ключу(2). После этого оба соединения будут ждать другой, чтобы отпереть замок на ключе. Это приводит к тупику.
Но, немного подправьте порядок транзакций, тогда тупиков можно избежать.
connection 1: locks key(1), locks key(2); connection 2: locks key(1), locks key(2);
Выше повторный порядок является доказательством тупика.
Другие способы избежать тупиков - это иметь механизм управления транзакциями. управление транзакциями по Spring-это почти plug-n-play. Кроме того, вы можете иметь политику повторных попыток блокировки на месте. Интересная тупиковая повторная попытка через Spring AOP может быть найдена здесь . Таким образом, вам просто нужно добавить аннотацию к методу, который вы хотите повторить в случае взаимоблокировки.Для получения дополнительных журналов отладки при взаимоблокировке, чтобы узнать, какие операторы подозрительны, попробуйте запустить диагностику" show engine innodb status". Кроме того, вы можете посмотреть на Как справиться с тупиками.
UPDATE: сценарий для взаимоблокировок в транзакционных операциях БД.
В транзакционной базе данных взаимоблокировка происходит, когда два каждый процесс в рамках своей транзакции обновляет две строки информации, но в обратном порядке. Например, процесс обновления строки 1 и строки 2 в точные временные рамки процесс В обновления строке 2 и строке 1. Процесс а не может завершить обновление строки 2 до завершения процесса В,но он не может завершить обновление строки 1 до завершения процесса А. Независимо от того, сколько времени пройдет, эта ситуация никогда не разрешится сама по себе, и из-за этого системы управления базами данных, как правило, убивают транзакция процесса, который выполнил наименьший объем работы.
Шишир