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 9

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 >
  1. могу ли я каким-то образом выполнить запрос обновления (либо JPA/Native) без необходимости блокировки таблицы через @Transactional?

Запросы обновления возможны через собственные запросы илиJPQL .

  1. могу ли я как-то войти в сеанс без использования @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 до завершения процесса А. Независимо от того, сколько времени пройдет, эта ситуация никогда не разрешится сама по себе, и из-за этого системы управления базами данных, как правило, убивают транзакция процесса, который выполнил наименьший объем работы.

Шишир