Режим XA — это стандарт распределённой транзакционной обработки, определённый организацией X/Open. Это можно считать одним из самых ранних стандартов в области распределённых транзакций, поэтому практически все основные базы данных (MySQL, Oracle...) поддерживают этот стандарт. Другими словами, эти базы данных уже могут реализовать распределённые транзакции на основе режима XA.
Этот режим представляет распределённые транзакции как два этапа
Первый этап: Этот этап также называется подготовительным. Управляющий транзакциями (Transaction Manager) отправляет запрос на подготовку к менеджеру ресурсов (Resource Manager), сообщая ему: "Вы можете выполнить бизнес-SQL, но не должны выполнять коммит после этого". Затем менеджер ресурсов выполняет операцию с базой данных и сообщает результат управляющему транзакциями (готово, если успешно; fail, если неудачно). Таким образом, управляющий транзакциями может определить, что делать на следующем этапе, основываясь на этих результатах.
Второй этап:
После получения обратной связи от менеджера ресурсов, управляющий транзакциями принимает решение. Если все операции были успешными, он сообщает всем менеджерам ресурсов: "Вы можете выполнить коммит". Затем менеджеры ресурсов получают запрос и выполняют коммит, завершая транзакцию. Если хотя бы одна операция не удалась, управляющий транзакциями сообщает всем менеджерам ресурсов выполнить откат.
Как видно, режим XA представляет собой сильную консистентность транзакций, но с мягкой доступностью, поскольку до выполнения коммита транзакции, база данных заблокирована и другие операции не могут быть выполнены.
Режим XA в Seata похож на вышеупомянутый режим XA, но включает понятие TM (Transaction Manager).
Первый этап: Seata фактически включает понятие TM, то есть управляющего транзакциями. TM регистрирует глобальную транзакцию как вход для распределённой транзакции, а затем вызывает различные ветвные транзакции. В каждой ветвной транзакции также есть RM (Resource Manager), который регистрирует ветвную транзакцию в TC (Transaction Coordinator), выполняет бизнес-SQL, но не выполняет коммит после этого, а просто отчитывается о состоянии транзакции. Таким образом, первый этап завершается.
Второй этап: После выполнения входного метода TM необходимо сообщить TC, что "все операции завершены, теперь все зависит от вас". Затем TC проверяет состояние отчетов по каждой ветви транзакций и отправляет сигнал RM — если первый этап успешно завершен, то транзакция будет подтверждена, в противном случае произойдет откат. После получения сигнала RM может начать процесс подтверждения или отката транзакции.#### 1.1.3 Преимущества и недостатки режима XA
Преимущества:
Поддержка сильной согласованности: после выполнения бизнес-SQL транзакция не подтверждается сразу, а ждет завершения координации TC, после чего происходит подтверждение или откат.
Простота реализации: поскольку база данных уже поддерживает режим XA, Seata просто добавляет простое обертывание над этим режимом. Если рассматривать только основные части, то различий практически нет.
Недостатки:
Низкая доступность и плохая производительность: первый этап не подтверждает транзакцию, а ждет второго этапа, что приводит к использованию блокировок базы данных (что является ненужным расходом системных ресурсов). В случае множества ветвей транзакций время выполнения бизнес-операций значительно увеличивается.
Зависимость от реализации базы данных: хотя использование и упрощено, если используется не SQL-база данных, как MySQL, а NoSQL-база данных, как Redis, это может не работать.
а) Изменение файла application (для каждого микросервиса, участвующего в транзакции) Включение режима XA в Seata
seata:
data-source-proxy-mode: XA # Включение режима XA для прокси-источника данныхб) Добавление аннотации @GlobalTransactional к входному методу, запускающему глобальную транзакцию.
В данном примере входной метод глобальной транзакции — это метод create в сервисе заказов (архитектура примера показана на следующем рисунке).Необходимо добавить аннотацию @GlobalTransactional
к методу create
.
@Override
@GlobalTransactional
public Long create(Order order) {
// Создание заказа
orderMapper.insert(order);
try {
// Уменьшение баланса пользователя
accountClient.deduct(order.getUserId(), order.getMoney());
// Уменьшение количества товара на складе
storageClient.deduct(order.getCommodityCode(), order.getCount());
} catch (FeignException e) {
log.error("Создание заказа не удалось, причина:{}", e.contentUTF8(), e);
throw new RuntimeException(e.contentUTF8(), e);
}
return order.getId();
}
AT-модель также является фазированной моделью транзакций. Её создание было вызвано необходимостью решения проблемы длительного периода блокировки ресурсов в модели XA.
AT-модель также состоит из двух фаз:
Первая фаза: TM запускает глобальную транзакцию как вход для распределённой транзакции, затем вызывает каждую ветвь транзакции. Каждый RM ветви регистрирует ветвь транзакции и выполняет локальное SQL-запрос, после чего сразу подтверждает (что отличается от модели XA). Поэтому AT-модель не требует блокировки ресурсов, что делает её более производительной по сравнению с моделью XA.
Но если одна из ветвей потерпит неудачу, как будет обеспечено откатывание?На самом деле, перед выполнением SQL-запроса RM создаёт снимок текущих данных, который называется "undo log". Это похоже на то, как Redis создаёт снимок при использовании метода RDB для сохранения состояния. Таким образом, даже если сервис перезапустится, данные можно будет восстановить посредством снимка. Если одна из ветвей транзакций потерпит неудачу при выполнении SQL-запроса, данные можно будет восстановить посредством снимка, и тогда можно смело подтверждать транзакцию.
Второй этап: Когда TM видит, что бизнес-транзакция завершена, он уведомляет TC, который затем решает, следует ли подтвердить или откатить изменения. Если состояние всех вложенных транзакций успешное, можно удалить подготовленные на первом этапе снимки (удаление снимков выполняется асинхронно, так как если первый этап прошёл успешно и был подтверждён, дальнейшие действия могут выполняться в отдельном потоке, что повышает производительность). Если на первом этапе были ошибки, данные нужно будет восстановить на основе undo-log, после чего лог становится ненужным и тоже удаляется.
В режиме XA откат транзакции зависит от механизмов базы данных, тогда как в режиме AT, поскольку транзакция уже была коммичена, её нельзя откатить. Вместо этого используется создание снимка данных для восстановления.
Режим XA обеспечивает строгую согласованность, в то время как режим AT обеспечивает конечную согласованность. Поскольку в режиме AT SQL-запросы выполняются и немедленно коммитируются, возможна ситуация, когда одни транзакции успешны, а другие нет, что приводит к несогласованному состоянию до момента восстановления данных на основе снимков.
После выполнения бизнес-SQL в режиме AT транзакция сразу коммитируется, что позволяет быстрее освобождать ресурсы, но также создаёт проблемы безопасности при высокой конкуренции.
Например, есть таблица баланса пользователя, содержащая поле money
, которое представляет собой баланс.Есть один поток, который открывает транзакцию 1, получает блокировку базы данных, создаёт снимок (money = 100
), выполняет бизнес-SQL (устанавливает money
равным 90) и коммитирует транзакцию, освобождая блокировку. В этот момент другой поток открывает транзакцию 2, получает блокировку, создаёт снимок (money = 90
), выполняет бизнес-SQL (устанавливает money
равным 80) и коммитирует транзакцию, освобождая блокировку. В процессе второго этапа, если транзакция 1 должна быть отменена (восстановление данных на основе снимка), то снимок транзакции 1 (money = 100
) будет неактуальным, что приведёт к проблеме грязной записи.#### 1.2.3 Решение: запись с изоляцией
Проблема, возникающая в вышеописанном процессе, связана с уровнем изоляции. Если первый и второй этапы будут находиться в состоянии блокировки, то другие транзакции не смогут вмешаться. Поэтому в режиме AT используется глобальная блокировка.
Примечание: Глобальная блокировка контролируется TC и используется для отслеживания текущего состояния транзакций, работающих с определённой строкой данных в определённой таблице. Эта таблица включает ID транзакции, имя таблицы и PK (первичный ключ) строки данных. С помощью глобальной блокировки можно гарантировать, что после выполнения бизнес-SQL транзакция 1 попытается получить глобальную блокировку. Это означает, что эта строка данных может быть владеена только транзакцией 1, и теперь можно безопасно завершить транзакцию.
Когда транзакция 2 завершает выполнение бизнес-SQL, она также пытается получить глобальную блокировку, но поскольку транзакция 1 уже получила блокировку, транзакция 2 будет заблокирована и будет ждать, пока транзакция 1 не освободит блокировку. Транзакция 1 должна дождаться окончания второго этапа, чтобы освободить глобальную блокировку, но в это время транзакция 2 будет владеть блокировкой базы данных, что приведёт к созданию замкнутого цикла блокировок. В конечном итоге транзакция 2 будет откатываться из-за превышения времени ожидания и освобождает блокировку базы данных.
Таким образом, это гарантирует, что во время выполнения первого и второго этапов транзакция 1 другие транзакции не смогут выполнять свои операции, обеспечивая хорошую изоляцию. **Вопрос 1: Некоторые могут сказать, что это не то же самое, что режим XA? Также блокируется ресурс, и тогда никто другой не сможет получить доступ к нему, а эффективность не упадет ли?**Здесь действительно есть различие в размере блока. В режиме XA блокировка базы данных не снимается, что означает, что никто другой не сможет получить доступ к данным этой базы данных. В то время как в режиме AT глобальная блокировка позволяет другим пользователям модифицировать все поля таблицы account
, кроме строки, которую вы изменяете. Таким образом, эффективность значительно выше.Вопрос 2: Некоторые могут сказать, что изоляция не полная, возможно, есть крайний случай, когда при изменении поля money
другой транзакции, не управляемой Seata, также пытается изменить это поле. В этом случае другой транзакции не требуется получать глобальную блокировку, и может возникнуть проблема грязной записи.
Действительно, это возможно, но вероятность очень мала, и вот почему:
Глобальные транзакции большинство времени успешно завершаются. Например, если я перевожу деньги, большинство времени я не буду знать, что денег недостаточно, чтобы перевести больше, чем имеется. Таким образом, второй этап почти всегда будет завершен без отката.
Распределённые транзакции обычно занимают много времени, или параллелизм низкий, поэтому такие "вклинивающиеся" транзакции редки.
Но даже если вероятность низкая, Seata всё равно учитывает эту ситуацию. Seata сохраняет два снимка состояния: первый — до обновления (money = 100), второй — после обновления (money = 90). Когда управляемая Seata транзакция достигает второго этапа, она сравнивает значения полей в базе данных с последним снимком. Если значения не совпадают, значит кто-то вмешался, и тогда система может отправить вам сообщение или электронное письмо, требуя вашего вмешательства.
Высокая производительность: после первого этапа транзакция сразу подтверждается, освобождая ресурсы рано и сокращая время блокировки базы данных.
Изоляция записей: использование механизма глобальной блокировки обеспечивает изоляцию, сохраняя при этом высокую производительность.
Простота использования, отсутствие необходимости изменения кода: Seata автоматически выполняет откат и подтверждение транзакций.
Недостатки:
Мягкое состояние: поскольку первый этап выполнения бизнес-операций SQL может привести к успешному завершению для одних операций и неудачному для других, и эти изменения уже подтверждены, согласованность достигается только во втором этапе через восстановление снимков.
Влияние на производительность: хотя производительность выше, чем в режиме XA, функция снимков в фреймворке все же влияет на производительность.
а) Создание таблиц
В режиме AT используется глобальная блокировка, поэтому требуется таблица lock_table для отслеживания (создается в базе данных, связанной с сервисом TC).sql DROP TABLE IF EXISTS lock_table; CREATE TABLE lock_table ( row_key VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, xid VARCHAR(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, transaction_id BIGINT(20) NULL DEFAULT NULL, branch_id BIGINT(20) NOT NULL, resource_id VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, table_name VARCHAR(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, pk VARCHAR(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, gmt_create DATETIME NULL DEFAULT NULL, gmt_modified DATETIME NULL DEFAULT NULL, PRIMARY KEY (row_key) USING BTREE, INDEX idx_branch_id(branch_id) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
Режим AT также требует сохранения снимков, поэтому необходима таблица undo_log
для отслеживания (внедряется в базу данных, связанную с микросервисом).
DROP TABLE IF EXISTS undo_log;
CREATE TABLE undo_log (
branch_id BIGINT(20) NOT NULL COMMENT 'branch transaction id',
xid VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
context VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'undo_log context, such as serialization',
rollback_info LONGBLOB NOT NULL COMMENT 'rollback info',
log_status INT(11) NOT NULL COMMENT '0: normal status, 1: defense status',
log_created DATETIME(6) NOT NULL COMMENT 'create datetime',
log_modified DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE INDEX ux_undo_log(xid, branch_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
б) Изменение файла application.yml
Для активации режима AT необходимо изменить конфигурацию транзакций.
seata:
data-source-proxy-mode: AT # Включение режима AT для прокси-соединения
в) Перезапуск сервиса и тестирование Следующие данные представляют собой баланс пользователя и количество товара на складе.
3. В процессе списания средств в сервисе списания происходит списание, после чего происходит откат.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )