От чего зависит, потребуется ли распределённая транзакция при атомарном изменении связанных данных в шардированной базе?
Потребность в распределённой транзакции определяется тем, на каких шардах находятся данные, которые должны измениться атомарно. Если связанные записи colocated на одном шарде, достаточно локальной транзакции; если они распределены между шардами, нужна координация вроде двухфазной фиксации, либо приходится менять бизнес-сценарий на сагу с компенсирующими действиями.
Шардирование появилось как способ масштабировать объём данных и нагрузку за пределы одного узла. Данные разделяют между узлами по ключу, чтобы операции над одной группой данных выполнялись локально и не требовали постоянной межузловой координации.
Проблема возникает, когда логически одна операция затрагивает записи с разными ключами распределения. Масштабирование повышает пропускную способность отдельных операций, но усложняет сохранение транзакционных гарантий между шардами.
Представим перевод средств: необходимо уменьшить баланс одного счёта и увеличить баланс другого. Если счета находятся на разных шардах, частичное выполнение недопустимо: списание без зачисления нарушит инвариант системы.
Неверно выбранный ключ распределения приводит к межшардовым транзакциям, дополнительным сетевым задержкам и более сложному восстановлению после сбоев. Попытка заменить атомарную транзакцию обычными последовательными запросами создаёт окно, в котором данные могут быть видимы в промежуточном состоянии.
При выборе ключа распределения сначала определяют границы транзакционной агрегатной единицы: набор данных, который должен изменяться атомарно. Если все записи этой единицы получают один ключ шардирования, операция выполняется на одном узле с обычными локальными гарантиями.
Если атомарно изменяемые данные имеют разные ключи, координатор может использовать двухфазную фиксацию. На первом этапе участники подготавливают изменения и гарантируют возможность фиксации, на втором координатор сообщает всем участникам принять или отклонить результат. Это сохраняет атомарность, но добавляет сетевые обмены, блокирование ресурсов или удержание состояния подготовки, а также усложняет поведение при отказе координатора.
Сага разбивает операцию на локальные транзакции. После успешного шага выполняется следующий, а при ошибке запускается компенсирующее действие. Сага обеспечивает согласование через конечное состояние, но не даёт мгновенной атомарности: между шагами возможны промежуточные состояния, повторные доставки и ошибки компенсации.
Co-location не является бесплатным решением. Если слишком много операций направить на один ключ, возникнет горячий шард; если выбрать слишком широкий ключ, возрастут межшардовые операции. Поэтому учитывают не только целостность, но и распределение нагрузки, размер агрегатов, шаблоны запросов, отказоустойчивость и возможность будущего перераспределения данных.
Сервис подписок хранит договор и его платежный статус. Эти записи часто обновляются одной бизнес-операцией, поэтому вариант с независимым хешированием идентификаторов договора и платежа создаёт межшардовую транзакцию. Вариант с размещением по идентификатору договора упрощает атомарное обновление, но крупный корпоративный договор может стать горячим ключом.
Рассматривались два решения. Двухфазная фиксация сохраняла строгую атомарность, но увеличивала задержку и зависимость от координатора. Сага лучше распределяла нагрузку, однако требовала обработки промежуточных статусов и повторяемых компенсаций.
Выбрали размещение данных договора и его платежного статуса на одном шарде, а редкие междоговорные операции сделали асинхронными через сагу. Для крупных клиентов добавили отдельную модель нагрузки и контроль горячих ключей. В результате основной путь остался локальным и атомарным, а редкие распределённые сценарии получили явно определённую eventual consistency.
Нет. Распределённая транзакция стремится обеспечить атомарный итог для всех участников одной операции. Сага сохраняет согласованность последовательностью локальных транзакций и компенсаций, поэтому допускает промежуточные состояния, задержки и ситуации, когда компенсация сама требует повторов или ручного разрешения.
Такой подход уменьшает межшардовую координацию, но может сконцентрировать нагрузку. Популярный клиент, крупный заказ или общий счёт способны создать горячий шард, ограничив производительность всей системы. Иногда приходится отделять редко изменяемые данные, распределять нагрузку отдельными ключами или принимать асинхронную согласованность для части сценариев.
Запросы важны, но ключ также определяет границы транзакций, равномерность нагрузки, размер перемещаемых данных и стоимость масштабирования. Ключ, идеально подходящий для чтения, может разнести атомарно изменяемые записи по узлам или создать неравномерную нагрузку. Его выбирают по совокупности транзакционных и эксплуатационных требований, а не по одному шаблону чтения.