Наконец-то кто-то дал понять о распределенных транзакциях!

Java

предисловие

Эта статья даст вам некоторое представление о распределенных транзакциях и объяснит принцип выполнения платформы обработки распределенных транзакций TX-LCN.Пожалуйста, поправьте меня, если я ошибаюсь.

1. Когда следует использовать распределенные транзакции?

Сценариев много, начнем с распространенного: в микросервисной системе, если бизнесу нужно использовать разные микросервисы, и разные микросервисы соответствуют разным базам данных.
Например: платформа электронной коммерции имеет бизнес-логику для клиентов, чтобы размещать заказы.Эта бизнес-логика включает в себя два микросервиса, один из которых — служба инвентаризации (запас минус один), а другой — служба заказов (количество заказов плюс один). ), схема выглядит следующим образом:
Если при выполнении этой бизнес-логики не используется распределенная транзакция, то при сбое одной из инвентаризаций и заказов, вероятно, значение базы данных инвентаризации уменьшится на 1, но база данных заказов не изменится; или инвентаризация не меняется. , есть еще один порядок, то есть есть явление несогласованности данных.
Поэтому в подобных ситуациях нам нужно использовать распределенные транзакции для обеспечения согласованности данных.

2. Решения для распределенных транзакций

2.1 Представлено: стратегия двухфазной фиксации в MySQL

Прежде чем говорить о решении распределенных транзакций, давайте посмотрим, как один источник данных выполняет обработку транзакций, и мы можем получить от этого некоторое вдохновение.
Возьмем в качестве примера движок MySQL InnoDB. Поскольку MySQL имеет два механизма журналов, один из которых является журналом повторов уровня хранилища, а другой — двоичным журналом уровня сервера. Каждый раз, когда данные обновляются, два журнала должны обновляться. . Чтобы предотвратить только одно из них, а не другое при записи журнала, MySQL использует метод, называемый двухфазной фиксацией, для обеспечения согласованности транзакций. Конкретно:
Предположим, вы создаете базу данных следующим образом: mysql> create table T(ID int primary key, c int); и затем выполните следующий оператор обновления: mysql> update T set c=c+1, где ID=2;
Поток выполнения этого оператора обновления выглядит следующим образом:
  1. Во-первых, исполнитель найдет движок для получения строки данных с ID=2.
  2. После получения данных он выполнит операцию +1 над данными, а затем вызовет интерфейс механизма для записи новых данных.
  3. Движок обновляет данные в память и записывает операцию в журнал повторов, который в это время находится в состоянии подготовки. Но он не фиксирует транзакцию, а просто сообщает исполнителю, что задача выполнена и может быть зафиксирована в любой момент.
  4. Исполнитель генерирует бинлог этой операции и записывает бинлог на диск
  5. Наконец, исполнитель вызывает интерфейс транзакций механизма, изменяет журнал повторов на состояние фиксации, и обновление завершается.
В приведенном выше процессе журнал повторов не отправляется напрямую после записи, а находится в состоянии подготовки.После уведомления исполнителя и записи бинлога журнал повторов будет отправлен снова. Этот процесс представляет собой двухэтапную фиксацию, которая представляет собой аккуратный дизайн.
Вы можете спросить, почему двухфазный коммит? Если двухэтапная фиксация не используется, то есть используется однофазная фиксация, что эквивалентно последовательной записи журнала повторов и бинлога.Если система выйдет из строя после записи журнала повторов, только журнал повторов запишет операцию , а бинлог зафиксирует операцию.Записи нет, что приводит к несогласованности данных, если используется двухэтапная фиксация, предполагая, что система дает сбой после записи журнала повторов, так как транзакция не была зафиксирована, ее можно откатить обратно плавно.
Каковы еще преимущества двухэтапного дизайна коммитов? Первое, что необходимо установить, — это концепция: чем дольше выполняется операция, тем больше вероятность того, что операция завершится ошибкой. Например, на то, чтобы поесть, уходит 20 минут, а на то, чтобы сходить в туалет, уходит 1. Вероятность получения сообщений WeChat во время еды определенно выше, чем вероятность получения сообщений WeChat во время похода в туалет. Поскольку время операции обновления в базе данных намного больше, чем время отправки транзакции, операция обновления выполняется первой, а транзакция отправляется после завершения всех трудоемких операций, что может обеспечить успешное выполнение транзакции. в наибольшей степени.

2.2 Стратегия двухэтапной фиксации для распределенных транзакций

В соответствии с приведенной выше стратегией двухэтапной фиксации распределенные транзакции также могут использовать аналогичный подход для завершения транзакции.
На первом этапе мы добавим роль менеджера транзакций, через которую будем координировать различные источники данных. Давайте сначала возьмем пример заказа.При выполнении логики размещения заказа, пусть каждая база данных выполняет свою собственную транзакцию, такую ​​как вычитание 1 из инвентаря и добавление 1 в библиотеку заказов, но не отправляйте ее после завершения , просто уведомите управление транзакцией о выполнении задачи.
На втором этапе, поскольку мы уже получили информацию о том, готов ли каждый источник данных на первом этапе, пока один источник данных не готов, все источники данных будут уведомлены об откате на втором этапе; если все данные источники готовы, затем уведомляет все источники данных о фиксации транзакции.
Обобщая процесс этой двухэтапной фиксации, таков: во-первых, диспетчер транзакций уведомляет каждый источник данных о необходимости работы и возвращает информацию о готовности. После того, как все источники данных готовы, уведомление о фиксации транзакции (откате) отправляется единообразно, чтобы позволить каждому источнику данных зафиксировать транзакцию. Поскольку окончательная операция фиксации занимает очень короткое время, вероятность сбоя операции мала.
Итак, каковы возможные недостатки этого протокола двухэтапной фиксации? Очень вероятно, что есть проблема с блокировкой.Если один из источников данных заблокирован из-за каких-то проблем, и нельзя вернуть ни информацию об успехе, ни о неудаче, то вся транзакция будет заблокирована. Соответствующая стратегия состоит в том, чтобы добавить некоторые операции обратного отсчета или повторно отправить сообщение.

3. Платформа распределенных транзакций TX-LCN

Обсудив столько теоретических знаний, давайте объясним принцип работы TX-LCN, платформы распределенных транзакций, которая действительно используется в производстве. (Типичная структура распределенных транзакций не ограничивается TX-LCN, такой как GTS Али, но GTS платная, а TX-LCN имеет открытый исходный код)
Давайте сначала посмотрим на принципиальную схему принципа работы, приведенную в официальном документе:
Идея аналогична двухэтапному процессу распределенной обработки транзакций, о котором мы упоминали выше (с небольшими отличиями), а основные этапы разделены на 3 этапа:
  1. Создайте группу транзакций. Прежде чем инициатор транзакции начнет выполнять бизнес-код, вызовите TxManager, чтобы создать объект группы транзакций, а затем получите процесс GroupId представления транзакции. Проще говоря, в центре управления транзакциями создается объект для операции выставления ордера, и получается id.
  2. Присоединитесь к группе транзакций: после того, как участник завершит выполнение бизнес-метода, он уведомляет TxManager о транзакционной информации модуля. То есть после того, как каждый источник данных (каждый сервис) завершит операцию, обратитесь в центр управления транзакциями и зарегистрируйтесь.
  3. Уведомление группы транзакций: после того, как инициатор выполнит бизнес-код, инициатор уведомляет TxManager о статусе результата выполнения инициатора. TxManager уведомит соответствующие участвующие модули о необходимости отправки или отката транзакции в соответствии с окончательным статусом транзакции и информацию о группе транзакций и вернуть результат инициатору транзакции. Служба заказов, которая имеет дело с клиентом, получит сообщение об успешном сокращении запасов и добавлении заказа.Он уведомит менеджера транзакций об этих двух сообщениях, а менеджер транзакций уведомит две службы инвентаризации для отправки транзакции или отката. сделка по ситуации.
В настоящее время в Интернете есть хорошая статья с анализом исходного кода выполнения TX-LCN.
Если вы будете следовать исходному коду в статье, вы обнаружите, что он похож на приведенную выше блок-схему, и в коде есть несколько замечательных мест, таких как:
public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable {
        if (Objects.isNull(DTXLocalContext.cur())) {
            DTXLocalContext.getOrNew();
        } else {
            return business.call();
        }
        log.debug("<---- TxLcn start ---->");
        DTXLocalContext dtxLocalContext = DTXLocalContext.getOrNew();
        TxContext txContext;
        // ---------- 保证每个模块在一个DTX下只会有一个TxContext ---------- //
        if (globalContext.hasTxContext()) {
            // 有事务上下文的获取父上下文
            txContext = globalContext.txContext();
            dtxLocalContext.setInGroup(true);
            log.debug("Unit[{}] used parent's TxContext[{}].", dtxInfo.getUnitId(), txContext.getGroupId());
        } else {
            // 没有的开启本地事务上下文
            txContext = globalContext.startTx();
        }
        //......
}

Этот код гарантирует, что под каждым модулем находится только один TxContext.Другими словами, если бизнес-логика не работает с разными источниками данных, а выполняет одну и ту же операцию несколько раз с одним и тем же источником данных, то модуль, соответствующий источнику данных В DTX будет только один TxContext.

Механизм координации транзакций LCN

Девиз LCN: LCN не производит транзакций, LCN является лишь координатором локальных транзакций. У всех должен быть вопрос, он не производит транзакций, так как же он контролирует каждый модуль, чтобы не отправлять сразу после завершения логической операции транзакции, а ждать, пока TxManager наконец не уведомит каждый модуль для отправки?
Поскольку каждый модуль является TxClient, под каждым TxClient существует пул соединений, который является пулом соединений, настроенным платформой, и использует статический прокси-сервер для переноса соединения.
public class LcnConnectionProxy implements Connection {
    private Connection connection;
    public LcnConnectionProxy(Connection connection) {
        this.connection = connection;
    }
    /**
     * notify connection
     *
     * @param state transactionState
     * @return RpcResponseState RpcResponseState
     */
    public RpcResponseState notify(int state) {
        try {
            if (state == 1) {
                log.debug("commit transaction type[lcn] proxy connection:{}.", this);
                connection.commit();
            } else {
                log.debug("rollback transaction type[lcn] proxy connection:{}.", this);
                connection.rollback();
            }
            connection.close();
            log.debug("transaction type[lcn] proxy connection:{} closed.", this);
            return RpcResponseState.success;
        } catch (Exception e) {
            log.error(e.getLocalizedMessage(), e);
            return RpcResponseState.fail;
        }
    }
    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        connection.setAutoCommit(false);
    }
    //......
}

Пул соединений всегда будет занимать ресурсы соединения этой распределенной транзакции, пока не получит транзакцию уведомления. Пока последний TxManager не уведомит TxClient, TxClient выполнит соответствующую фиксацию или откат. Таким образом, механизм координации транзакций LCN эквивалентен перехвату пула соединений и контролю отправки транзакции соединения.

Механизм компенсации транзакций LCN

Поскольку мы не можем гарантировать, что транзакция выполняется нормально каждый раз, если бизнес-метод выполняется, транзакция должна быть выполнена успешно, но транзакция не отправляется нормально из-за зависания сервера или нестабильности сети и т. д. Этот сценарий необходимо завершено компенсационными делами.
В этом случае TxManager сделает отметку и вернется к инициатору. Сообщите ему, что эта транзакция не была уведомлена, а затем TxClient снова выполнит транзакцию запроса.

Наконец

Прошу всех обратить внимание на мой паблик [Программист в погоне за ветром], в нем будут обновляться статьи, а также размещаться отсортированная информация.