Конфигурация стратегии параллелизма для 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. Краткое описание того, как это работает:
- В этом режиме Hibernate сам управляет транзакциями. Все действия БД должны быть внутри транзакции, режим автоматической фиксации не будет работать.
- В течение
flush()
(который может появляться несколько раз в течение срока действия транзакции, но обычно происходит непосредственно перед фиксацией) Hibernate проходит сеанс и ищет обновленные / вставленные / удаленные объекты. Затем эти объекты сначала сохраняются в базе данных, а затем блокируются и обновляются в кэше, поэтому одновременные транзакции не могут ни обновлять, ни считывать их. - Если транзакция затем откатывается (явным образом или из-за какой-либо ошибки), заблокированные объекты просто освобождаются и удаляются из кэша, поэтому другие транзакции могут читать / обновлять их.
- Если транзакция зафиксирована успешно, заблокированные объекты просто освобождаются, и другие потоки могут читать / записывать их.
Здесь есть пара тонкостей:
Возможно повторяемое нарушение чтения. Представьте, что у нас есть Транзакция A (tA) и Транзакция B (tB), которые запускаются одновременно, и оба загружают объект X, tA затем модифицирует этот объект, а затем tA фиксируется. Во многих базах данных, которые используют изоляцию моментальных снимков (Oracle, PostgreSQL, FireBird), если tB снова запрашивает объект X, он должен получить то же состояние объекта, что и в начале транзакции. Однако кэш READ_WRITE может нарушать это условие - там нет изоляции моментального снимка. Hibernate пытается обойти это, используя временные метки на кешированных объектах, но в операционных системах с плохим разрешением таймера (15,6 мс в Windows) гарантированно пропускаются некоторые гонки.
Возможные оптимистические версии устаревших объектов - возможно получить устаревшие версии объектов, если вам очень не повезло работать в Windows, и у вас есть несколько транзакций, совершенных с одной и той же отметкой времени.