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

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

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

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

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

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

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

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

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

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

Названия полей

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

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

Индексы

Снижение задержки записи

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

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

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

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

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

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

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

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

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

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

Поля TTL

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

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

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

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

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

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

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

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

SDK и клиентские библиотеки Cloud Firestore автоматически повторяют неудачные транзакции для обработки временных ошибок. Если ваше приложение обращается к Cloud Firestore напрямую через REST или RPC API, а не через 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 .