Сериализуемость и изоляция транзакций

На этой странице описываются конфликты транзакционных данных, сериализуемость и изоляция. Примеры кода для транзакций можно найти в статье Транзакции и пакетные записи.

Транзакции и конфликты данных

Чтобы транзакция была успешной, документы, полученные в результате операций чтения, не должны быть изменены операциями вне транзакции. Если другая операция попытается изменить один из этих документов, она вступит в состояние конкуренции за данные с транзакцией.

Конкуренция за данные
Когда две или более операции конкурируют за управление одним и тем же документом. Например, для одной транзакции может потребоваться, чтобы документ оставался согласованным, в то время как параллельная операция пытается обновить значения полей этого документа.

Cloud Firestore разрешает конфликт данных, задерживая или отменяя одну из операций. Клиентские библиотеки Cloud Firestore автоматически повторяют транзакции, которые не удалось выполнить из-за конфликта данных. После определенного количества попыток операция транзакции завершается неудачно и возвращает сообщение об ошибке:

ABORTED: Too much contention on these documents. Please try again.

Решение о том, какую операцию отменить или отложить, зависит от типа управления параллелизмом.

Управление параллелизмом

Режим параллелизма – это настраиваемый параметр базы данных. Cloud Firestore поддерживает следующие режимы параллелизма:

  • PESSIMISTIC: пессимистичное управление параллелизмом предполагает, что конкуренция за данные вероятна. Пессимистичные транзакции используют блокировки базы данных, чтобы предотвратить изменение данных другими операциями.

    При пессимистичном управлении параллелизмом транзакции блокируют документы, которые они считывают. Блокировка документа транзакцией не позволяет другим транзакциям, пакетным записям и записям, не связанным с транзакциями, изменять этот документ. Транзакция снимает блокировку документов во время фиксации. Блокировка также снимается, если время ожидания истекло или произошел сбой.

    Когда транзакция блокирует документ, другие операции записи должны дождаться, пока транзакция снимет блокировку. Транзакции получают блокировки в хронологическом порядке.

  • OPTIMISTIC: Оптимистичное управление параллелизмом предполагает, что конкуренция за данные маловероятна или что удерживать блокировки базы данных неэффективно. Оптимистичные транзакции не используют блокировки базы данных, чтобы запретить другим операциям изменять данные.

    При оптимистичном управлении параллелизмом транзакция отслеживает все документы, которые вы читаете внутри транзакции. Транзакция завершает операции записи, только если ни один из этих документов не изменился во время ее выполнения. Если какой-либо документ изменился, обработчик транзакции повторит транзакцию. Если после нескольких попыток транзакция не может получить чистый результат, она завершается неудачно из-за конфликта данных.

Режим параллельной работы по умолчанию

Для версии Standard по умолчанию установлено значение PESSIMISTIC. Значение по умолчанию для версии Enterprise – OPTIMISTIC. Однако поведение также зависит от типа клиентской библиотеки:

  • В SDK для мобильных устройств и веб-приложений используется оптимистичное управление параллелизмом. SDK для мобильных устройств и веб-приложений работают независимо от этой настройки, поскольку они всегда эмулируют оптимистичный параллелизм.
  • Клиентские библиотеки сервера используют элементы управления параллелизмом, заданные в настройках базы данных.

Режим параллельного просмотра

Чтобы посмотреть режим параллелизма на стороне сервера, выполните команду gcloud firestore databases describe.

gcloud firestore databases describe \
  --project=PROJECT_ID \
  --database=DATABASE_ID

Изменить режим параллельной работы

Выполните команду gcloud firestore databases update, чтобы изменить режим параллелизма на стороне сервера для базы данных:

gcloud firestore databases update \
  --project=PROJECT_ID \
  --database=DATABASE_ID \
  --concurrency-mode=CONCURRENCY_MODE

Значения:

  • CONCURRENCY_MODE – PESSIMISTIC или OPTIMISTIC.
  • PROJECT_ID – идентификатор проекта Google Cloud.
  • DATABASE_ID – идентификатор базы данных Cloud Firestore.

Конкуренция за данные в SDK для мобильных устройств и веб-приложений

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

В SDK для мобильных устройств и веб-приложений используется оптимистичное управление параллелизмом, поскольку они могут работать в средах с высокой задержкой и ненадежным сетевым подключением. Блокировка документов в среде с высокой задержкой приведет к слишком большому количеству сбоев из-за конфликтов данных.

Конкуренция за данные в клиентских библиотеках сервера

Серверные клиентские библиотеки (C#, Go, Java, Node.js, PHP, Python, Ruby) используют функцию встроенных транзакций. В этих транзакциях используется настройка режима параллелизма на уровне базы данных, а значение по умолчанию зависит от версии:

  • В версии Enterprise по умолчанию используется оптимистичное управление параллелизмом, чтобы поддерживать операции, которые сканируют целые коллекции. Оптимистичное управление параллелизмом помогает избежать операций сканирования, которые блокируют большое количество документов.

  • В версии Standard используется пессимистичное управление параллелизмом, а также предполагается низкая задержка и надежное подключение к базе данных.

Уровень изоляции Serializable

Конкуренция за данные между транзакциями тесно связана с уровнями изоляции базы данных. Уровень изоляции базы данных описывает, насколько хорошо система справляется с конфликтами между параллельными операциями. Конфликт возникает из-за следующих требований к базе данных:

  • Для транзакций требуются точные и согласованные данные.
  • Для эффективного использования ресурсов базы данных выполняют операции параллельно.

В системах с низким уровнем изоляции операция чтения в рамках транзакции может считывать неточные данные из незафиксированных изменений в параллельной операции.

Сериализуемая изоляция определяет самый высокий уровень изоляции. Сериализуемая изоляция означает, что:

  • Вы можете предположить, что база данных выполняет транзакции последовательно.
  • На транзакции не влияют незафиксированные изменения в параллельных операциях.

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

Cloud Firestore гарантирует сериализуемую изоляцию транзакций. Транзакции в Cloud Firestore сериализуются и изолируются по времени фиксации.

Сериализуемая изоляция по времени фиксации

Cloud Firestore назначает каждой транзакции время фиксации, которое представляет собой единую точку во времени. Когда Cloud Firestore фиксирует изменения транзакции в базе данных, можно считать, что все операции чтения и записи в рамках транзакции выполняются точно в момент фиксации.

Фактическое выполнение транзакции занимает некоторое время. Выполнение транзакции начинается до времени фиксации, и выполнение нескольких операций может перекрываться. Cloud Firestore поддерживает сериализуемую изоляцию и гарантирует, что:

  • Cloud Firestore фиксирует транзакции в порядке времени фиксации.
  • Cloud Firestore изолирует транзакции от параллельных операций с более поздним временем фиксации.

В случае конфликта данных между параллельными операциями Cloud Firestore использует оптимистичное и пессимистичное управление параллелизмом для разрешения конфликта.

Изоляция в рамках транзакции

Изоляция транзакций также применяется к операциям записи в рамках транзакции. Запросы и операции чтения в транзакции не видят результаты предыдущих операций записи в этой транзакции. Даже если вы измените или удалите документ в рамках транзакции, все операции чтения документов в этой транзакции вернут версию документа на момент фиксации, до операций записи транзакции. Если документ не существовал на указанный момент времени, операции чтения ничего не возвращают.

Проблемы с конфликтами данных

Дополнительную информацию о конфликтах данных и способах их устранения можно найти на странице устранения неполадок.