Конфигурация стратегии параллелизма для JBoss TreeCache в качестве кэша Hibernate 2-го уровня

Я использую JBoss EAP 4.3.

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

Для каждой сущности я могу установить одно из следующих значений "использования" в @Cache аннотация: NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL,

С другой стороны, по моему JBossTreeCache файл конфигурации я могу установить IsolationLevel для одного из следующих для всего кэша: NONE, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE (или просто использовать OPTIMISTIC).

Если рассматривать параметры конфигурации по одному, документация достаточно ясна, но мне интересно, что происходит, когда вы комбинируете различные параметры.

Например, если вы установите @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) для сущности, но настроить NONE как IsolationLevel для JBossTreecache, что просходит?

Я также считаю, что JBossTreeCache только поддерживает NONE, READ_ONLY а также TRANSACTIONAL использование, но что IsolationLevel Вам разрешено объединять их с? И что произойдет, если вы используете, например, NONSTRICT_READ_WRITE?

Всего здесь должно быть 5х6 различных комбинаций, но не все из них имеют смысл.

Может ли кто-нибудь помочь мне разобраться с этим?

1 ответ

Уровень изоляции - сложная проблема.

Например, если вы установите @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) для сущности, но настроить NONE как IsolationLevel для JBossTreecache, что просходит?

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

Хорошо, о комбинациях: настройка кэша только для чтения в Hibernate должна использоваться, когда ваши объекты не меняются. Например, для словаря страны. Уровень параллелизма кэша NONE или READ_ONLY должен использоваться вместе с ним.

Не строгое чтение-запись следует использовать, когда ваши кешированные объекты меняются, но это случается редко, и шансы для условий гонки невелики. Например, для словаря часовых поясов - часовые пояса могут иногда появляться / исчезать, но это может происходить пару раз в год. Опять же, уровень параллелизма кэша NONE или READ_ONLY должен использоваться вместе с ним.

Теперь о более интересных комбинациях.

Transactional кеши в Hibernate НЕ безопасны, Hibernate предполагает, что обновления кеша являются транзакционными, но ничего не делает для их обеспечения. Таким образом, вы ДОЛЖНЫ использовать полноценного внешнего координатора XA (распределенных транзакций), и вы действительно действительно не хотите этого, если вы действительно не знаете, что делаете. Скорее всего, вам придется использовать полный контейнер EJB3 для поддержки XA-менеджера, хотя можно использовать внешний менеджер транзакций, например http://www.atomikos.com/ с простыми сервлетами + Spring. Очевидно, вам нужно использовать TRANSACTIONAL кеши с этим.

READ_WRITE - интересная комбинация. В этом режиме Hibernate сам работает как легкий XA-координатор, поэтому он не требует полноценного внешнего XA. Краткое описание того, как это работает:

  1. В этом режиме Hibernate сам управляет транзакциями. Все действия БД должны быть внутри транзакции, режим автоматической фиксации не будет работать.
  2. В течение flush() (который может появляться несколько раз в течение срока действия транзакции, но обычно происходит непосредственно перед фиксацией) Hibernate проходит сеанс и ищет обновленные / вставленные / удаленные объекты. Затем эти объекты сначала сохраняются в базе данных, а затем блокируются и обновляются в кэше, поэтому одновременные транзакции не могут ни обновлять, ни считывать их.
  3. Если транзакция затем откатывается (явным образом или из-за какой-либо ошибки), заблокированные объекты просто освобождаются и удаляются из кэша, поэтому другие транзакции могут читать / обновлять их.
  4. Если транзакция зафиксирована успешно, заблокированные объекты просто освобождаются, и другие потоки могут читать / записывать их.

Здесь есть пара тонкостей:

Возможно повторяемое нарушение чтения. Представьте, что у нас есть Транзакция A (tA) и Транзакция B (tB), которые запускаются одновременно, и оба загружают объект X, tA затем модифицирует этот объект, а затем tA фиксируется. Во многих базах данных, которые используют изоляцию моментальных снимков (Oracle, PostgreSQL, FireBird), если tB снова запрашивает объект X, он должен получить то же состояние объекта, что и в начале транзакции. Однако кэш READ_WRITE может нарушать это условие - там нет изоляции моментального снимка. Hibernate пытается обойти это, используя временные метки на кешированных объектах, но в операционных системах с плохим разрешением таймера (15,6 мс в Windows) гарантированно пропускаются некоторые гонки.

Возможные оптимистические версии устаревших объектов - возможно получить устаревшие версии объектов, если вам очень не повезло работать в Windows, и у вас есть несколько транзакций, совершенных с одной и той же отметкой времени.

Другие вопросы по тегам