Для устранения проблем, связанных с медленными запросами, используйте функцию Query Explain , чтобы получить план выполнения запроса и профиль выполнения во время выполнения. В следующем разделе описаны шаги, которые можно предпринять для оптимизации производительности запросов в зависимости от профиля выполнения:
Ограничьте количество результатов
Используйте поле «Records Return» в дереве выполнения, чтобы определить, возвращает ли запрос много документов. Рассмотрите возможность ограничения количества возвращаемых документов с помощью этапа limit(...) . Это уменьшит сериализованный размер результатов в байтах при передаче клиентам по сети. В случаях, когда узлу Limit предшествует узел MajorSort , механизм запросов может объединить узлы Limit и MajorSort и заменить полную материализацию и сортировку в памяти сортировкой TopN, что уменьшит требования к памяти для запроса.
Ограничьте размер результирующего документа.
Рекомендуется ограничить размер возвращаемого документа, используя оператор select(...) для возврата только необходимых полей или remove_fields(...) для отбрасывания слишком больших полей. Это помогает снизить вычислительные и оперативные затраты на обработку промежуточных результатов, а также размер сериализованных результатов в байтах при передаче клиентам по сети. В случаях, когда все поля, на которые ссылается запрос, охвачены обычным индексом, это также позволяет полностью охватить запрос сканированием индекса, избегая необходимости извлечения документов из основного хранилища.
Используйте индексы
Для настройки и оптимизации индексов воспользуйтесь приведенными ниже инструкциями.
Определите, использует ли запрос индекс.
Определить, использует ли запрос индекс, можно, проверив конечные узлы в дереве выполнения. Если конечный узел дерева выполнения является узлом TableScan , это означает, что запрос не использует индекс и сканирует документы из основного хранилища. Если индекс используется, конечный узел дерева выполнения отобразит идентификатор индекса и поля индекса.
Определите более подходящий индекс
Индекс полезен для запроса, если он может уменьшить количество документов, которые механизму запросов необходимо извлекать из основного хранилища, или если порядок полей в индексе обеспечивает выполнение требований запроса к сортировке.
Если для запроса используется индекс, но механизм запросов по-прежнему извлекает и отбрасывает множество документов, о чем свидетельствует узел Scan, возвращающий много записей, за которым следует узел Filter , возвращающий мало записей, это признак того, что предикат запроса, удовлетворяемый с помощью индекса, не является селективным. Чтобы создать более подходящий индекс, см. раздел «Создание индексов» .
Если для запроса используется индекс, но механизм обработки запросов по-прежнему выполняет переупорядочивание результирующего набора в памяти, что определяется узлом MajorSort в дереве выполнения запроса, это означает, что используемый индекс не может обеспечить выполнение требований сортировки запроса. Для создания более подходящего индекса см. следующий раздел.
Создание индексов
Для создания индексов следуйте инструкциям в документации по управлению индексами. Чтобы ваш запрос мог использовать индексы, создайте обычные (не мультиключевые) индексы с полями в следующем порядке:
- Все поля, которые будут использоваться в операторах равенства. Для максимального увеличения вероятности повторного использования в разных запросах, упорядочивайте поля в порядке убывания частоты их появления в операторах равенства в разных запросах.
- Все поля, по которым будет производиться сортировка (в том же порядке).
- Поля, которые будут использоваться в операторах диапазона или неравенства, в порядке убывания избирательности ограничений запроса.
- Поля, которые будут возвращены в рамках запроса в индексе: включение таких полей в индекс позволяет индексу охватывать запрос и избежать необходимости извлечения документа из основного хранилища.
Принудительное сканирование индекса или таблицы
При выполнении запросов Cloud Firestore автоматически используются индексы, которые могут повысить эффективность запроса. В результате вам не нужно указывать индекс для ваших запросов. Однако для запросов, критически важных для вашей рабочей нагрузки, мы рекомендуем использовать параметр forceIndex для более стабильной производительности.
В некоторых случаях Cloud Firestore может выбрать индекс, который приводит к увеличению задержки выполнения запроса. Если вы выполнили шаги по устранению неполадок, связанных с ухудшением производительности, и подтвердили, что имеет смысл попробовать другой индекс для запроса, вы можете указать индекс с помощью параметра forceIndex .
В операциях Pipeline на любом этапе ввода можно использовать параметр forceIndex , чтобы переопределить план запроса по умолчанию Cloud Firestore и указать используемый индекс или принудительно выполнить сканирование таблицы.
Принудительное указание конкретного индекса
Чтобы принудительно использовать определенный индекс в запросе, укажите идентификатор индекса в виде строки в параметре forceIndex . Идентификатор индекса можно найти в консоли или в сообщениях об ошибках.
Следующий пример заставляет планировщик использовать индекс с идентификатором CICAgOi36pgK :
Node.js
// Force Planner to use Index ID CICAgOi36pgK await db.pipeline() .collectionGroup({ collectionId: "customers", forceIndex: "CICAgOi36pgK" }) .limit(100) .execute();
Java
// Force Planner to use Index ID CICAgOi36pgK Pipeline.Snapshot results1 = firestore.pipeline() .collectionGroup("customers", new CollectionGroupOptions() .withHints(new CollectionHints().withForceIndex("CICAgOi36pgK"))) .limit(100) .execute().get();
Идти
// Force Planner to use Index ID CICAgOi36pgK snapshot1 := client.Pipeline(). CollectionGroup("customers", firestore.WithForceIndex("CICAgOi36pgK")). Limit(100). Execute(ctx)
Вот несколько примеров использования принудительного указания конкретного индекса:
- Проверка эффективности различных индексов.
- Обеспечение использования для запроса конкретного, заведомо оптимального индекса.
- Переопределение оптимизатора, если его выбор по умолчанию является неоптимальным для конкретного запроса.
Если указанный индекс не найден, запрос завершится ошибкой.
Принудительное сканирование таблицы
Сканирование таблицы считывает документы из коллекции или группы коллекций без использования каких-либо вторичных индексов. Чтобы принудительно выполнить сканирование таблицы, установите forceIndex в primary .
Следующий пример принудительно выполняет сканирование таблицы:
// Force Planner to only do a Full-Table Scan
db.pipeline()
.collectionGroup({ collectionId: "customers", forceIndex: "primary" })
.limit(100)
Сканирование таблицы может быть использовано в следующих случаях:
- Для очень небольших коллекций, где накладные расходы на индексирование не оправданы.
- Для запросов, которые обращаются к большинству документов в коллекции.
- Для отладки и сравнения производительности.
Используйте forceIndex с Query Explain.
Для наблюдения за эффектами forceIndex можно использовать Query Explain , особенно с опцией analyze :
- Убедитесь, что Cloud Firestore использовал указанный индекс в
forceIndex, проверив идентификатор индекса в конечных узлах дерева выполнения. - Убедитесь, что узел
TableScanприсутствует в плане выполнения при использованииforceIndex: "primary". - Сравните показатели производительности — такие как задержка, количество отсканированных документов и количество отсканированных записей индекса — с использованием и без использования
forceIndex, чтобы точно настроить производительность запросов.
Рекомендации по использованию forceIndex
Хотя forceIndex предоставляет больший контроль над выполнением запросов, оптимизатор запросов Cloud Firestore , как правило, эффективен для большинства сценариев использования. При использовании forceIndex следует учитывать следующие рекомендации:
- Используйте
forceIndexс осторожностью. Если вы наблюдаете неоптимальную производительность при использовании плана запроса по умолчанию, используйте Query Explain для диагностики проблемы, прежде чем принудительно создавать индекс. - При использовании
forceIndexобязательно тестируйте свои запросы с реалистичными объемами данных, чтобы понять их производительность и стоимость. - В производственных средах избегайте использования
forceIndex: "primary"для больших коллекций данных.