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

Прочитайте этот документ, чтобы принять обоснованные решения по архитектуре ваших приложений для обеспечения высокой производительности и надежности. Этот документ содержит расширенные темы 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 (службу Cloud Firestore в данном контексте). Она также предоставляет другие важные функции, включая защиту от атак типа «отказ в обслуживании».

Cloud 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 с разделением. Разделение реплицируется в трех различных зонах, и каждое разделение имеет назначенного лидера Paxos.

Макет данных

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

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

Единое региональное местоположение — это определенное географическое местоположение, например 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 (или более) разных зонах. Каждый раздел имеет лидера Paxos, который может находиться в другой зоне для разных разделов.

<класс диапазона= Разделение базы данных Cloud Firestore">

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

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

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

Изменить на документ в коллекции

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

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

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

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

  1. Клиент хранилища выдает коммит. Коммит содержит мутации M1-M5.
  2. Участниками этой транзакции являются Splits 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 , определяется параметрами чтения для запроса Read.

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

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

Сильные чтения

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

Одиночное разделение чтения

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

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

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

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

Многократное чтение

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

Устаревшие чтения

Strong reads — это режим по умолчанию в 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 автоматически индексирует все поля в документе по умолчанию, такие перемещающиеся хотспоты могут также создаваться в индексном пространстве для поля документа, которое содержит последовательно увеличивающееся/убывающее значение, например, временную метку.

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

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

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

Что дальше?