Распространение транзакций
Распространение определяет границу транзакции нашей бизнес-логики. Spring удается запустить и приостановить транзакцию в соответствии с нашими настройками распространения .
Spring вызывает TransactionManager::getTransaction , чтобы получить или создать транзакцию в соответствии с распространением. Он поддерживает некоторые варианты распространения для всех типов TransactionManager , но некоторые из них поддерживаются только определенными реализациями TransactionManager .
Давайте рассмотрим различные способы распространения и то, как они работают.
1. REQUIRED
REQUIRED — это распространение по умолчанию. Spring проверяет, существует ли активная транзакция, и, если ничего не существует, создает новую. В противном случае бизнес-логика добавляется к текущей активной транзакции.
2. SUPPORTS
Для SUPPORTS Spring сначала проверяет, существует ли активная транзакция. Если транзакция существует, то будет использоваться существующая транзакция. Если транзакции нет, она выполняется нетранзакционно.
3. MANDATORY
Когда распространение MANDATORY, если есть активная транзакция, то она будет использоваться. Если активной транзакции нет, Spring выдает исключение.
4. NEVER
Для транзакционной логики с распространением NEVER Spring выдает исключение, если есть активная транзакция.
5. NOT_SUPPORTED
Если текущая транзакция существует, сначала Spring приостанавливает ее, а затем бизнес-логика выполняется без транзакции.
Примечание: JTATransactionManager поддерживает реальную приостановку транзакций «из коробки». Другие имитируют приостановку, сохраняя ссылку на существующую ссылку и затем удаляя ее из контекста потока.
6. REQUIRES_NEW
Когда распространение REQUIRES_NEW , Spring приостанавливает текущую транзакцию, если она существует, а затем создает новую.
Примечание: Как и в случае с NOT_SUPPORTED , нам нужен JTATransactionManager для фактической приостановки транзакции.
7. NESTED
Для распространения NESTED Spring проверяет, существует ли транзакция, и если да, то отмечает точку сохранения. Это означает, что если выполнение нашей бизнес-логики выдает исключение, транзакция откатывается к этой точке сохранения. Если активной транзакции нет, она работает как REQUIRED .
Изоляция транзакций
Изоляция — это одно из распространенных свойств ACID: атомарность, согласованность, изоляция и долговечность. Изоляция описывает, как изменения, примененные параллельными транзакциями, видны друг другу.
Каждый уровень изоляции предотвращает нулевые или более побочные эффекты параллелизма в транзакции:
- Грязное чтение: прочитать незафиксированное изменение параллельной транзакции.
- Неповторяемое чтение : получение другого значения при повторном чтении строки, если параллельная транзакция обновляет одну и ту же строку и фиксирует ее.
- Фантомное чтение: получение разных строк после повторного выполнения запроса диапазона, если другая транзакция добавляет или удаляет некоторые строки в диапазоне и фиксирует их.
Мы можем установить уровень изоляции транзакции с помощью @Transactional::isolation. В Spring есть пять перечислений: DEFAULT , READ_UNCOMMITTED , READ_COMMITTED , REPEATABLE_READ , SERIALIZABLE
1. DEFAULT
Уровень изоляции по умолчанию — DEFAULT . В результате, когда Spring создает новую транзакцию, уровень изоляции будет изоляцией по умолчанию для нашей СУБД. Поэтому нам следует быть осторожными, если мы меняем базу данных.
Также следует рассмотреть случаи, когда мы вызываем цепочку методов с разной изоляцией . В обычном потоке изоляция применяется только при создании новой транзакции. Таким образом, если по какой-либо причине мы не хотим, чтобы метод выполнялся в другой изоляции, мы должны установить для TransactionManager::setValidateExistingTransaction значение true.
2. READ_UNCOMMITTED
READ_UNCOMMITTED — это самый низкий уровень изоляции, обеспечивающий максимально одновременный доступ.
В результате он страдает от всех трех упомянутых побочных эффектов параллелизма. Транзакция с такой изоляцией считывает незафиксированные данные других параллельных транзакций. Кроме того, могут произойти как неповторяющиеся, так и фантомные чтения. Таким образом, мы можем получить другой результат при повторном чтении строки или повторном выполнении запроса диапазона.
Примечание: Postgres не поддерживает изоляцию READ_UNCOMMITTED и вместо этого возвращается к READ_COMMITED . Кроме того, Oracle не поддерживает и не разрешает READ_UNCOMMITTED .
3. READ_COMMITTED
Второй уровень изоляции, READ_COMMITTED, предотвращает грязное чтение.
Остальные побочные эффекты параллелизма все еще могут иметь место. Таким образом, незафиксированные изменения в параллельных транзакциях не оказывают на нас никакого влияния, но если транзакция фиксирует свои изменения, наш результат может измениться при повторном запросе.
Примечание: READ_COMMITTED — это уровень по умолчанию в Postgres, SQL Server и Oracle.
4. REPEATABLE_READ
Третий уровень изоляции, REPEATABLE_READ, предотвращает грязное и неповторяющееся чтение. Таким образом, на нас не влияют незафиксированные изменения в параллельных транзакциях.
Кроме того, когда мы повторно запрашиваем строку, мы не получаем другого результата. Однако при повторном выполнении запросов к диапазону мы можем получить новые добавленные или удаленные строки.
Более того, это самый низкий уровень, необходимый для предотвращения потери обновления. Потерянное обновление происходит, когда две или более одновременные транзакции читают и обновляют одну и ту же строку. REPEATABLE_READ вообще не разрешает одновременный доступ к строке. Следовательно, потерянное обновление не может произойти.
Примечание: REPEATABLE_READ — это уровень по умолчанию в Mysql. Oracle не поддерживает REPEATABLE_READ.
5. SERIALIZABLE
SERIALIZABLE — это высший уровень изоляции. Он предотвращает все упомянутые побочные эффекты параллелизма, но может привести к самой низкой скорости одновременного доступа, поскольку параллельные вызовы выполняются последовательно.
Другими словами, одновременное выполнение группы сериализуемых транзакций дает тот же результат, что и их последовательное выполнение.