Каков "лучший" способ распределенных транзакций между несколькими базами данных с использованием Spring и Hibernate?
У меня есть приложение - больше похоже на утилиту - которое сидит в углу и периодически обновляет две разные базы данных.
Это небольшое автономное приложение, созданное с помощью Spring Application Context. В контексте настроены две фабрики сеансов Hibernate, в свою очередь использующие источники данных Commons DBCP, настроенные в Spring.
В настоящее время нет управления транзакциями, но я хотел бы добавить некоторые из них. Обновление одной базы данных зависит от успешного обновления другой.
Приложение не находится в контейнере Java EE - оно загружается статическим классом запуска, вызываемым из сценария оболочки. Класс запуска создает экземпляр Application Context, а затем вызывает метод на одном из его bean-компонентов.
Каков "лучший" способ обеспечить транзакционность обновлений базы данных?
Я оставлю вам определение "лучший", но я думаю, что это должна быть функция "легко настроить", "легко настроить", "недорого" и "легко упаковывать и распространять". Естественно, FOSS был бы хорош.
6 ответов
Лучший способ распределить транзакции по нескольким базам данных: не надо.
Некоторые люди укажут вам на XA, но XA (или Two Phase Commit) - ложь (или маркетолог).
Представьте себе: после того, как на первом этапе менеджер XA сообщил, что он может отправить окончательную фиксацию, сетевое соединение с одной из баз данных перестает работать. Что теперь? Тайм-аут? Это оставит другую базу данных поврежденной. Откат? Две проблемы: вы не можете откатить коммит и как узнать, что случилось со второй базой данных? Может быть, сетевое соединение не удалось после того, как он успешно зафиксировал данные, и было потеряно только сообщение об успехе?
Лучший способ - скопировать данные в одном месте. Используйте схему, которая позволяет прервать копию и продолжить ее в любое время (например, игнорировать уже имеющиеся данные или заказать выбор по идентификатору и запрашивать только записи> MAX(ID) вашей копии). Защитите это с помощью транзакции. Это не проблема, поскольку вы только читаете данные из источника, поэтому, когда транзакция по какой-либо причине завершается неудачей, вы можете игнорировать исходную базу данных. Следовательно, это простая старая транзакция с одним источником.
После того, как вы скопировали данные, обработайте их локально.
Настройте менеджер транзакций в вашем контексте. Весенние документы имеют примеры, и это очень просто. Затем, когда вы хотите выполнить транзакцию:
try {
TransactionTemplate tt = new TransactionTemplate(txManager);
tt.execute(new TransactionCallbackWithoutResult(){
protected void doInTransactionWithoutResult(
TransactionStatus status) {
updateDb1();
updateDb2();
}
} catch (TransactionException ex) {
// handle
}
Дополнительные примеры и информацию, возможно, смотрите на это: транзакции XA с использованием Spring
Когда вы говорите "две разные базы данных", подразумеваете ли вы разные серверы баз данных или две разные схемы в одном и том же сервере БД?
Если первое, то если вам нужна полная транзакционность, вам нужен API транзакции XA, который обеспечивает полную двухфазную фиксацию. Но что еще более важно, вам также необходим координатор / монитор транзакций, который управляет распространением транзакций между различными системами баз данных. Это часть спецификации JavaEE, и в этом довольно редкая часть. Координатор TX сам по себе является сложным программным обеспечением. Ваша прикладная программа (через Spring, если вы того пожелаете) общается с координатором.
Однако, если вы просто имеете в виду две базы данных на одном и том же сервере БД, тогда ванильные JDBC-транзакции должны работать просто отлично, просто выполняйте операции с обеими базами данных в рамках одной транзакции.
В этом случае вам потребуется Transaction Monitor (сервер, поддерживающий протокол XA) и убедитесь, что ваши базы данных также поддерживают XA. Большинство (все?) J2EE-серверов поставляется со встроенным Transaction Monitor. Если ваш код работает не на J2EE-сервере, то есть множество автономных альтернатив - Atomicos, Bitronix и т. Д.
Вы можете попробовать Spring ChainedTransactionManager - http://docs.spring.io/spring-data/commons/docs/1.6.2.RELEASE/api/org/springframework/data/transaction/ChainedTransactionManager.html который поддерживает распределенную транзакцию БД. Это может быть лучшей альтернативой XA
Тем, кто предлагает отказаться от проблем с двухфазной фиксацией, поскольку она широко используется на практике, я предлагаю посмотреть это: https://en.wikipedia.org/wiki/Two-phase_commit_protocol. Внизу статьи о 2PC есть ссылка на статью о трехфазной фиксации (!)
Некоторые выдержки из статьи о 3PC:
В компьютерных сетях и базах данных протокол трехфазной фиксации (3PC)[1] представляет собой распределенный алгоритм, который позволяет всем узлам распределенной системы согласиться на совершение транзакции. Это более устойчивая к сбоям модификация протокола двухфазной фиксации (2PC).
Трехфазная фиксация предполагает сеть с ограниченной задержкой и узлами с ограниченным временем ответа; В большинстве практических систем с неограниченной сетевой задержкой и паузами процессов это не может гарантировать атомарность.
Обобщить:
- 3PC более отказоустойчив, чем 2PC
- Даже 3PC не гарантирует атомарность
- Выводы о 2PC делайте сами.