Понимание операций чтения и записи в масштабе

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

Cloud Firestore — это гибкая масштабируемая база данных для разработки мобильных устройств, Интернета и серверов от Firebase и Google Cloud. Начать работу с Cloud Firestore и писать многофункциональные и мощные приложения очень легко.

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

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

Понимание компонентов высокого уровня

На следующей диаграмме показаны компоненты высокого уровня, участвующие в запросе API Cloud Firestore.

Компоненты высокого уровня

Cloud Firestore SDK и клиентские библиотеки

Cloud Firestore поддерживает SDK и клиентские библиотеки для разных платформ. Хотя приложение может выполнять прямые вызовы HTTP и RPC к API Cloud Firestore, клиентские библиотеки предоставляют уровень абстракции для упрощения использования API и реализации лучших практик. Они также могут предоставлять дополнительные функции, такие как автономный доступ, кэширование и т. д.

Интерфейс Google (GFE)

Это инфраструктурный сервис, общий для всех облачных сервисов Google. GFE принимает входящие запросы и перенаправляет их в соответствующую службу Google (в данном контексте службу Firestore). Он также предоставляет другие важные функции, включая защиту от атак типа «отказ в обслуживании».

Облачный сервис Firestore

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

Уровень хранения Cloud Firestore

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

Ключевые диапазоны и разделения

Cloud Firestore — это документально-ориентированная база данных NoSQL. Данные хранятся в документах , которые организованы в иерархии коллекций . Иерархия коллекции и идентификатор документа преобразуются в один ключ для каждого документа. Документы логически хранятся и лексикографически упорядочиваются по этому единственному ключу. Мы используем термин «диапазон ключей» для обозначения лексикографически непрерывного диапазона ключей.

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

Синхронная репликация

Важно отметить, что база данных всегда реплицируется автоматически и синхронно. Разделения данных имеют реплики в разных зонах , чтобы они оставались доступными, даже если зона становится недоступной. Согласованная репликация на разные копии разделения управляется алгоритмом консенсуса Paxos . Одна реплика каждого разделения выбирается в качестве лидера Paxos, который отвечает за обработку записей в этом разделе. Синхронная репликация дает вам возможность всегда читать последнюю версию данных из Cloud Firestore.

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

Расположение данных

Cloud Firestore — это бессхемная база данных документов. Однако внутри он размещает данные в основном в двух таблицах в стиле реляционной базы данных на уровне хранения следующим образом:

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

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

Расположение данных

Один регион против нескольких регионов

При создании базы данных необходимо выбрать регион или несколько регионов .

Единственное региональное местоположение — это определенное географическое местоположение, например us-west1 . Разделения данных базы данных Cloud Firestore имеют реплики в разных зонах выбранного региона, как объяснялось ранее.

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

Дополнительную информацию о местонахождении региона см. в разделе «Местоположения Cloud Firestore» .

Один регион против нескольких регионов

Понимание жизни записи в Cloud Firestore

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

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

Шаги высокого уровня в транзакции записи

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

На первом этапе транзакции Cloud Firestore считывает существующий документ и определяет изменения, которые необходимо внести в данные в таблице «Документы».

Это также включает внесение необходимых обновлений в таблицу индексов следующим образом:

  • Поля, добавляемые в документы, требуют соответствующих вставок в таблицу «Индексы».
  • Поля, удаляемые из документов, требуют соответствующего удаления в таблице «Индексы».
  • Поля, которые изменяются в документах, требуют как удаления (для старых значений), так и вставки (для новых значений) в таблицу индексов.

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

После расчета мутаций Cloud Firestore собирает их внутри транзакции, а затем фиксирует ее.

Понимание транзакции записи на уровне хранения

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

На следующей диаграмме база данных Cloud Firestore имеет восемь разделений (отмечено 1–8), размещенных на трех разных серверах хранения в одной зоне, и каждое разделение реплицируется в 3 (или более) разных зонах. У каждого раскола есть лидер Паксоса, который может находиться в разных зонах для разных расколов.

Разделение базы данных Cloud Firestore

Рассмотрим базу данных Cloud Firestore, содержащую коллекцию Restaurants , следующим образом:

Коллекция ресторанов

Клиент Cloud Firestore запрашивает следующее изменение документа в коллекции Restaurant , обновляя значение поля priceCategory .

Перейти к документу в коллекции

Следующие шаги высокого уровня описывают, что происходит во время записи:

  1. Создайте транзакцию чтения-записи.
  2. Прочтите документ restaurant1 в коллекции Restaurants из таблицы Documents на уровне хранения.
  3. Прочтите индексы документа из таблицы Индексы .
  4. Вычислите мутации, которые необходимо внести в данные. В данном случае имеется пять мутаций:
    • M1: обновите строку restaurant1 в таблице «Документы» , чтобы отразить изменение значения поля priceCategory .
    • M2 и M3: удалите строки для старого значения priceCategory в таблице индексов для нисходящих и возрастающих индексов.
    • M4 и M5: вставьте строки для нового значения priceCategory в таблицу индексов для нисходящих и возрастающих индексов.
  5. Совершите эти мутации.

Клиент хранилища в сервисе Cloud Firestore ищет разделения, которым принадлежат ключи строк, которые необходимо изменить. Давайте рассмотрим случай, когда Split 3 обслуживает M1, а Split 6 обслуживает M2-M5. Существует распределенная транзакция, в которой в качестве участников участвуют все эти разделения. Разделения участников могут также включать в себя любое другое разделение, из которого данные были считаны ранее в рамках транзакции чтения-записи.

Следующие шаги описывают, что происходит в рамках фиксации:

  1. Клиент хранилища выдает фиксацию. Коммит содержит мутации M1-M5.
  2. Сплиты 3 и 6 являются участниками этой транзакции. Один из участников выбирается в качестве координатора , например Split 3. Задача координатора — обеспечить, чтобы транзакция либо фиксировалась, либо прерывалась атомарно для всех участников.
    • Реплики-лидеры этих сплитов несут ответственность за работу, проделанную участниками и координаторами.
  3. Каждый участник и координатор запускает алгоритм Paxos со своими соответствующими репликами.
    • Лидер запускает алгоритм Paxos с репликами. Кворум достигается, если большинство реплик ответят ok to commit ответа лидеру.
    • Затем каждый участник уведомляет координатора о своей готовности (первая фаза двухфазной фиксации). Если какой-либо участник не может подтвердить транзакцию, вся транзакция aborts .
  4. Как только координатор узнает, что все участники, включая его самого, готовы, он сообщает результат accept транзакции всем участникам (вторая фаза двухфазной фиксации). На этом этапе каждый участник записывает решение о фиксации в стабильное хранилище, и транзакция фиксируется.
  5. Координатор отвечает клиенту хранилища в Cloud Firestore, что транзакция зафиксирована. Параллельно координатор и все участники применяют мутации к данным.

Жизненный цикл фиксации

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

Пишет в мультирегионе

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

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

Каждая запись в Cloud Firestore также предполагает некоторое взаимодействие с механизмом реального времени в Cloud Firestore. Дополнительные сведения о запросах реального времени см. в разделе Понимание запросов реального времени в масштабе .

Поймите жизнь чтения в Cloud Firestore

В этом разделе рассматриваются автономные чтения не в реальном времени в Cloud Firestore. Внутренне сервер Cloud Firestore обрабатывает большинство этих запросов в два основных этапа:

  1. Одно сканирование диапазона по таблице индексов.
  2. Точечные поиски в таблице «Документы» на основе результатов предыдущего сканирования.
В Cloud Firestore могут быть определенные запросы, которые требуют меньше или больше обработки (например, запросы IN).

Чтение данных с уровня хранения выполняется внутри системы с использованием транзакции базы данных для обеспечения согласованного чтения. Однако, в отличие от транзакций, используемых для записи, эти транзакции не используют блокировки. Вместо этого они выбирают временную метку, а затем выполняют все операции чтения в эту временную метку. Поскольку они не устанавливают блокировки, они не блокируют одновременные транзакции чтения и записи. Чтобы выполнить эту транзакцию, клиент хранилища в Cloud Firestore указывает привязку к временной метке, которая сообщает уровню хранения, как выбрать временную метку чтения. Тип временной метки, выбранный клиентом хранилища в Cloud Firestore, определяется параметрами чтения для запроса на чтение.

Понимание транзакции чтения на уровне хранения

В этом разделе описываются типы операций чтения и способы их обработки на уровне хранения в Cloud Firestore.

Сильное чтение

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

Чтение с одним разделением

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

На этом этапе в зависимости от выбранной реплики могут произойти следующие случаи:

  • Запрос на чтение поступает к реплике-лидеру (зона A).
    • Поскольку лидер всегда в курсе событий, чтение может быть продолжено напрямую.
  • Запрос на чтение отправляется на реплику, не являющуюся ведущей (например, зону B).
    • Разделение 3 может знать по своему внутреннему состоянию, что у него достаточно информации для обслуживания чтения, и разделение делает это.
    • Группа Split 3 не уверена, получила ли она последние данные. Он отправляет сообщение лидеру с просьбой указать временную метку последней транзакции, которую необходимо применить для обслуживания чтения. Как только эта транзакция будет применена, чтение может продолжиться.

Cloud Firestore затем возвращает ответ своему клиенту.

Мультираздельное чтение

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

устаревшее чтение

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

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

Избегайте горячих точек

Разделения в Cloud Firestore автоматически разбиваются на более мелкие части, чтобы распределить работу по обслуживанию трафика на большее количество серверов хранения, когда это необходимо или когда пространство ключей расширяется. Сплиты, созданные для обработки избыточного трафика, сохраняются примерно в течение 24 часов, даже если трафик пропадает. Таким образом, если возникают повторяющиеся всплески трафика, разделения сохраняются, и при необходимости вводятся дополнительные разделения. Эти механизмы помогают базам данных Cloud Firestore автоматически масштабироваться при увеличении нагрузки на трафик или размера базы данных. Однако существуют некоторые ограничения, о которых следует знать, как описано ниже.

Разделение хранилища и нагрузки требует времени, а слишком быстрое увеличение трафика может привести к ошибкам с высокой задержкой или превышением крайнего срока, обычно называемым « горячими точками» , пока служба адаптируется. Лучше всего распределить операции по диапазону ключей, одновременно увеличивая трафик коллекции в базе данных до 500 операций в секунду. После постепенного увеличения трафика увеличивайте его на 50 % каждые пять минут. Этот процесс называется правилом 500/50/5 и обеспечивает оптимальное масштабирование базы данных в соответствии с вашей рабочей нагрузкой.

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

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

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

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

Поиск неисправностей

Firestore предоставляет Key Visualizer в качестве диагностического инструмента, специально предназначенного для анализа моделей использования и устранения проблем с горячими точками.

Что дальше