Лучшие практики для Cloud Firestore

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

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

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

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

Выберите региональное расположение , чтобы снизить затраты, снизить задержку записи, если ваше приложение чувствительно к задержке, или разместить его вместе с другими ресурсами GCP .

Идентификаторы документов

  • Избегайте идентификаторов документов . и .. .
  • Избегайте использования косой черты / в идентификаторах документов.
  • Не используйте монотонно увеличивающиеся идентификаторы документов, такие как:

    • Customer1 , Customer2 , Customer3 , ...
    • Product 1 , Product 2 , Product 3 , ...

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

Имена полей

  • Избегайте использования следующих символов в именах полей, поскольку они требуют дополнительного экранирования:

    • . период
    • [ левая скобка
    • ] правая скобка
    • * звездочка
    • ` обратный апостроф

Индексы

Уменьшите задержку записи

Основной причиной задержки записи является разветвление индекса. Лучшие методы уменьшения разветвления индекса:

  • Установите исключения для индекса на уровне коллекции . Простой вариант по умолчанию — отключить индексацию по убыванию и массиву. Удаление неиспользуемых индексированных значений также снизит затраты на хранение .

  • Уменьшите количество документов в транзакции. Для написания большого количества документов рассмотрите возможность использования массового средства записи вместо атомарного пакетного средства записи.

Исключения из индекса

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

Случай Описание
Большие строковые поля

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

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

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

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

Поля TTL

Если вы используете политики TTL (время жизни) , обратите внимание, что поле TTL должно быть меткой времени. Индексирование полей TTL включено по умолчанию и может повлиять на производительность при более высоких скоростях трафика. Рекомендуется добавить исключения для отдельных полей для полей TTL.

Большой массив или поля карты

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

Операции чтения и записи

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

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

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

Повторные попытки транзакций

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

Обновления в реальном времени

Рекомендации по обновлению в реальном времени см. в разделе Понимание запросов реального времени в масштабе .

Масштабное проектирование

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

Обновления в одном документе

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

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

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

Избегайте высоких скоростей чтения или записи для лексикографического закрытия документов, иначе в вашем приложении возникнут конфликтные ошибки. Эта проблема известна как «горячая точка», и ваше приложение может столкнуться с «горячей точкой», если оно выполняет любое из следующих действий:

  • Создает новые документы с очень высокой скоростью и выделяет свои собственные монотонно увеличивающиеся идентификаторы.

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

  • Создает новые документы с высокой скоростью в коллекции с небольшим количеством документов.

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

  • Удаляет документы в коллекции с высокой скоростью.

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

Не пропускайте удаленные данные

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

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

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Каждый раз, когда этот запрос выполняется, он сканирует записи индекса для created поля во всех недавно удаленных документах. Это замедляет запросы.

Чтобы повысить производительность, используйте метод start_at чтобы найти лучшее место для начала. Например:

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

ПРИМЕЧАНИЕ. В приведенном выше примере используется монотонно увеличивающееся поле, что является антишаблоном для высоких скоростей записи.

Увеличение трафика

Вам следует постепенно увеличивать трафик к новым коллекциям или лексикографически закрывать документы, чтобы дать Cloud Firestore достаточно времени для подготовки документов к увеличению трафика. Мы рекомендуем начинать с максимум 500 операций в секунду до новой коллекции, а затем увеличивать трафик на 50% каждые 5 минут. Вы также можете увеличить объем трафика записи, но помните о стандартных ограничениях Cloud Firestore . Убедитесь, что операции распределены относительно равномерно по всему диапазону клавиш. Это называется правилом «500/50/5».

Перенос трафика в новую коллекцию

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

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

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

Параллельное чтение

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

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

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

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

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

Конфиденциальность

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

Предотвратить несанкционированный доступ

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

Узнайте больше об использовании Cloud Firestore Security Rules .

,

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

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

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

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

Выберите региональное расположение , чтобы снизить затраты, снизить задержку записи, если ваше приложение чувствительно к задержке, или разместить его вместе с другими ресурсами GCP .

Идентификаторы документов

  • Избегайте идентификаторов документов . и .. .
  • Избегайте использования косой черты / в идентификаторах документов.
  • Не используйте монотонно увеличивающиеся идентификаторы документов, такие как:

    • Customer1 , Customer2 , Customer3 , ...
    • Product 1 , Product 2 , Product 3 , ...

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

Имена полей

  • Избегайте использования следующих символов в именах полей, поскольку они требуют дополнительного экранирования:

    • . период
    • [ левая скобка
    • ] правая скобка
    • * звездочка
    • ` обратный апостроф

Индексы

Уменьшите задержку записи

Основной причиной задержки записи является разветвление индекса. Лучшие методы уменьшения разветвления индекса:

  • Установите исключения для индекса на уровне коллекции . Простой вариант по умолчанию — отключить индексацию по убыванию и массиву. Удаление неиспользуемых индексированных значений также снизит затраты на хранение .

  • Уменьшите количество документов в транзакции. Для написания большого количества документов рассмотрите возможность использования массового средства записи вместо атомарного пакетного средства записи.

Исключения из индекса

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

Случай Описание
Большие строковые поля

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

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

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

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

Поля TTL

Если вы используете политики TTL (время жизни) , обратите внимание, что поле TTL должно быть меткой времени. Индексирование полей TTL включено по умолчанию и может повлиять на производительность при более высоких скоростях трафика. Рекомендуется добавить исключения для отдельных полей для полей TTL.

Большой массив или поля карты

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

Операции чтения и записи

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

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

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

Повторные попытки транзакций

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

Обновления в реальном времени

Рекомендации по обновлению в реальном времени см. в разделе Понимание запросов реального времени в масштабе .

Масштабное проектирование

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

Обновления в одном документе

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

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

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

Избегайте высоких скоростей чтения или записи для лексикографического закрытия документов, иначе в вашем приложении возникнут конфликтные ошибки. Эта проблема известна как «горячая точка», и ваше приложение может столкнуться с «горячей точкой», если оно выполняет любое из следующих действий:

  • Создает новые документы с очень высокой скоростью и выделяет свои собственные монотонно увеличивающиеся идентификаторы.

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

  • Создает новые документы с высокой скоростью в коллекции с небольшим количеством документов.

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

  • Удаляет документы в коллекции с высокой скоростью.

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

Не пропускайте удаленные данные

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

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

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Каждый раз, когда этот запрос выполняется, он сканирует записи индекса для created поля во всех недавно удаленных документах. Это замедляет запросы.

Чтобы повысить производительность, используйте метод start_at чтобы найти лучшее место для начала. Например:

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

ПРИМЕЧАНИЕ. В приведенном выше примере используется монотонно увеличивающееся поле, что является антишаблоном для высоких скоростей записи.

Увеличение трафика

Вам следует постепенно увеличивать трафик к новым коллекциям или лексикографически закрывать документы, чтобы дать Cloud Firestore достаточно времени для подготовки документов к увеличению трафика. Мы рекомендуем начинать с максимум 500 операций в секунду до новой коллекции, а затем увеличивать трафик на 50% каждые 5 минут. Вы также можете увеличить объем трафика записи, но помните о стандартных ограничениях Cloud Firestore . Убедитесь, что операции распределены относительно равномерно по всему диапазону клавиш. Это называется правилом «500/50/5».

Перенос трафика в новую коллекцию

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

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

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

Параллельное чтение

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

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

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

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

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

Конфиденциальность

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

Предотвратите несанкционированный доступ

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

Узнайте больше об использовании Cloud Firestore Security Rules .