Поделиться через


Диагностика и устранение проблем с запросами при использовании Azure Cosmos DB для MongoDB

Область применения: MongoDB

В этой статье рассматривается общий рекомендуемый подход для устранения проблем с запросами в Azure Cosmos DB. Хотя не следует думать, что действия, описанные в этой статье, дают полную защиту от потенциальных проблем с запросами, мы включили сюда наиболее распространенные советы по повышению производительности. Эту статью следует использовать в качестве отправного пункта для устранения проблем с ресурсоемкими или дорогостоящими запросами в API Azure Cosmos DB для MongoDB. Если вы используете Azure Cosmos DB для NoSQL, ознакомьтесь со статьей по устранению неполадок с запросами NoSQL.

Оптимизации запросов в Azure Cosmos DB распределены по категориям следующим образом.

  • Оптимизация, снижающая затраты единиц запроса (RU) на запрос.
  • Оптимизация, которая просто сокращает задержку.

Сокращение количества единиц запроса также обычно сокращает задержку.

В этой статье приведены примеры, которые можно повторно создать с помощью набора данных о питании.

Примечание.

В этой статье предполагается, что вы используете учетные записи API Azure Cosmos DB для MongoDB с версией 3.6 и более поздними версиями. Некоторые запросы, которые плохо работают в версии 3.2, имеют значительные улучшения в версии 3.6 и более поздних версиях. Выполните обновление до версии 3.6, подав запрос на поддержку.

Чтобы получить метрики, используйте команду $explain

При оптимизации запроса в Azure Cosmos DB первым шагом всегда является получение стоимости в ЕЗ для запроса. Не вдаваясь в детали, рекомендуется снизить размер оплаты за ЕЗ, превышающие 50 ЕЗ.

Помимо получения оплаты за ЕЗ, команда $explain позволяет получить метрики использования запросов и индексов. Ниже приведен пример, в котором выполняется запрос и используется команда $explain для отображения метрик использования запросов и индексов.

Команда $explain:

db.coll.find({foodGroup: "Baby Foods"}).explain({"executionStatistics": true })

Выходные данные:

{
    "stages" : [ 
        {
            "stage" : "$query",
            "timeInclusiveMS" : 905.2888,
            "timeExclusiveMS" : 905.2888,
            "in" : 362,
            "out" : 362,
            "details" : {
                "database" : "db-test",
                "collection" : "collection-test",
                "query" : {
                    "foodGroup" : {
                        "$eq" : "Baby Foods"
                    }
                },
                "pathsIndexed" : [],
                "pathsNotIndexed" : [ 
                    "foodGroup"
                ],
                "shardInformation" : [ 
                    {
                        "activityId" : "e68e6bdd-5e89-4ec5-b053-3dbbc2428140",
                        "shardKeyRangeId" : "0",
                        "durationMS" : 788.5867,
                        "preemptions" : 1,
                        "outputDocumentCount" : 362,
                        "retrievedDocumentCount" : 8618
                    }
                ],
                "queryMetrics" : {
                    "retrievedDocumentCount" : 8618,
                    "retrievedDocumentSizeBytes" : 104963042,
                    "outputDocumentCount" : 362,
                    "outputDocumentSizeBytes" : 2553535,
                    "indexHitRatio" : 0.0016802042237178,
                    "totalQueryExecutionTimeMS" : 777.72,
                    "queryPreparationTimes" : {
                        "queryCompilationTimeMS" : 0.19,
                        "logicalPlanBuildTimeMS" : 0.14,
                        "physicalPlanBuildTimeMS" : 0.09,
                        "queryOptimizationTimeMS" : 0.03
                    },
                    "indexLookupTimeMS" : 0,
                    "documentLoadTimeMS" : 687.22,
                    "vmExecutionTimeMS" : 774.09,
                    "runtimeExecutionTimes" : {
                        "queryEngineExecutionTimeMS" : 37.45,
                        "systemFunctionExecutionTimeMS" : 10.82,
                        "userDefinedFunctionExecutionTimeMS" : 0
                    },
                    "documentWriteTimeMS" : 49.42
                }
            }
        }
    ],
    "estimatedDelayFromRateLimitingInMilliseconds" : 0.0,
    "continuation" : {
        "hasMore" : false
    },
    "ok" : 1.0
}

Выходные данные команды $explain довольно обширны и содержат подробные сведения о выполнении запроса. Однако в общем случае при оптимизации производительности запросов следует обратить внимание на несколько разделов:

Метрическая Description
timeInclusiveMS Задержка запросов в серверной части
pathsIndexed Индексы, используемые запросом
pathsNotIndexed Индексы, которые мог бы использовать запрос, если он доступен
shardInformation Сводка по производительности запросов для определенного физического раздела
retrievedDocumentCount Количество документов, загруженных обработчиком запросов
outputDocumentCount Количество документов, возвращенных обработчиком запросов
estimatedDelayFromRateLimitingInMilliseconds Оценка дополнительной задержки запросов из-за ограничения скорости

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

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

Плата в единицах запроса слишком высока.

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

Число полученных документов приблизительно равно числу выходных документов

Плата в единицах запроса приемлема, но задержка по-прежнему слишком высока.

Запросы, в которых количество полученных документов превышает число выходных документов

retrievedDocumentCount — это число документов, которые нужно было загрузить подсистеме обработки запросов. outputDocumentCount — это количество документов, возвращенных запросом. Если retrievedDocumentCount значительно выше, чем outputDocumentCount, то по крайней мере одна часть запроса не смогла использовать индекс и вынуждена была провести сканирование.

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

Команда $explain:

db.coll.find(
  {
    $and : [
            { "foodGroup" : "Cereal Grains and Pasta"}, 
            { "description" : "Oat bran, cooked"}
        ]
  }
).explain({"executionStatistics": true })

Выходные данные:

{
    "stages" : [ 
        {
            "stage" : "$query",
            "timeInclusiveMS" : 436.5716,
            "timeExclusiveMS" : 436.5716,
            "in" : 1,
            "out" : 1,
            "details" : {
                "database" : "db-test",
                "collection" : "indexing-test",
                "query" : {
                    "$and" : [ 
                        {
                            "foodGroup" : {
                                "$eq" : "Cereal Grains and Pasta"
                            }
                        }, 
                        {
                            "description" : {
                                "$eq" : "Oat bran, cooked"
                            }
                        }
                    ]
                },
                "pathsIndexed" : [],
                "pathsNotIndexed" : [ 
                    "foodGroup", 
                    "description"
                ],
                "shardInformation" : [ 
                    {
                        "activityId" : "13a5977e-a10a-4329-b68e-87e4f0081cac",
                        "shardKeyRangeId" : "0",
                        "durationMS" : 435.4867,
                        "preemptions" : 1,
                        "outputDocumentCount" : 1,
                        "retrievedDocumentCount" : 8618
                    }
                ],
                "queryMetrics" : {
                    "retrievedDocumentCount" : 8618,
                    "retrievedDocumentSizeBytes" : 104963042,
                    "outputDocumentCount" : 1,
                    "outputDocumentSizeBytes" : 6064,
                    "indexHitRatio" : 0.0,
                    "totalQueryExecutionTimeMS" : 433.64,
                    "queryPreparationTimes" : {
                        "queryCompilationTimeMS" : 0.12,
                        "logicalPlanBuildTimeMS" : 0.09,
                        "physicalPlanBuildTimeMS" : 0.1,
                        "queryOptimizationTimeMS" : 0.02
                    },
                    "indexLookupTimeMS" : 0,
                    "documentLoadTimeMS" : 387.44,
                    "vmExecutionTimeMS" : 432.93,
                    "runtimeExecutionTimes" : {
                        "queryEngineExecutionTimeMS" : 45.36,
                        "systemFunctionExecutionTimeMS" : 16.86,
                        "userDefinedFunctionExecutionTimeMS" : 0
                    },
                    "documentWriteTimeMS" : 0.13
                }
            }
        }
    ],
    "estimatedDelayFromRateLimitingInMilliseconds" : 0.0,
    "continuation" : {
        "hasMore" : false
    },
    "ok" : 1.0
}

Значение retrievedDocumentCount (8618) значительно выше, чем outputDocumentCount (1), что подразумевает, что для этого запроса требуется сканирование документа.

Добавьте необходимые индексы

Необходимо проверить массив pathsNotIndexed и добавить эти индексы. В этом примере пути foodGroup и description должны быть проиндексированы.

"pathsNotIndexed" : [ 
                    "foodGroup", 
                    "description"
                ]

Рекомендации по индексированию для API Azure Cosmos DB для MongoDB отличаются от таковых для собственно MongoDB. В API Azure Cosmos DB для MongoDB составные индексы используются только в запросах, которые должны эффективно сортироваться по нескольким свойствам. При наличии запросов с фильтрами по нескольким свойствам необходимо создать индексы одного поля для каждого из этих свойств. В предикатах запросов можно использовать несколько индексов одного поля.

Индексы с подстановочными знаками могут упростить индексирование. В отличие от MongoDB, индексы с подстановочными знаками могут поддерживать несколько полей в предикатах запросов. При использовании одного индекса с подстановочными знаками вместо создания отдельного индекса для каждого свойства различия в производительности запросов не будет. Добавление индекса с подстановочными знаками для всех свойств — самый простой способ оптимизации всех запросов.

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

Сведения об операциях агрегирования, использующих индекс

В большинстве случаев операции агрегирования в API Azure Cosmos DB для MongoDB будут частично использовать индексы. Как правило, обработчик запросов будет применять фильтры по равенству и диапазону первыми и использовать индексы. После применения этих фильтров обработчик запросов может оценить дополнительные фильтры и прибегнуть к загрузке оставшихся документов для выполнения статистического вычисления, если это необходимо.

Приведем пример:

db.coll.aggregate( [
   { $match: { foodGroup: 'Fruits and Fruit Juices' } },
   {
     $group: {
        _id: "$foodGroup",
        total: { $max: "$version" }
     }
   }
] )

В этом случае индексы могут оптимизировать этап $match. Добавление индекса для foodGroup значительно повысит производительность запросов. Как и в MongoDB, для максимально эффективного использования индексов следует как можно раньше поместить $match в конвейер агрегирования.

В API Azure Cosmos DB для MongoDB индексы не используются для фактической статистической обработки, которая в данном случае имеет значение $max. Добавление индекса для version не повысит производительность запросов.

Запросы, в которых количество полученных документов равно числу выходных документов

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

Сведите к минимуму число запросов между секциями.

Azure Cosmos DB использует секционирование для масштабирования отдельных контейнеров по мере роста числа единиц запросов и потребностей хранилища данных. Каждый физическая секция имеет отдельный и независимый индекс. Если запрос имеет фильтр проверки на равенство, соответствующий ключу секции контейнера, необходимо проверить только индекс соответствующей секции. Эта оптимизация сокращает общее число единиц запроса, необходимых для запроса. Узнайте больше о различиях между запросами в разделах и запросами между разделами.

При наличии большого количества подготовленных единиц запроса (более 30 000) или большого объема хранимых данных (более чем примерно 100 ГБ) контейнер, вероятно, будет достаточно велик, чтобы увидеть значительное стоимости в единицах запроса.

Чтобы понять метрики запросов для каждого отдельного физического раздела, можно изучить массив shardInformation. Число уникальных значений shardKeyRangeId — это количество физических разделов, в которых необходимо выполнить запрос. В этом примере запрос выполнялся в четырех физических разделах. Важно понимать, что выполнение полностью независимо от использования индексов. Иными словами, запросы между разделами по-прежнему могут использовать индексы.

  "shardInformation" : [ 
                    {
                        "activityId" : "42f670a8-a201-4c58-8023-363ac18d9e18",
                        "shardKeyRangeId" : "5",
                        "durationMS" : 24.3859,
                        "preemptions" : 1,
                        "outputDocumentCount" : 463,
                        "retrievedDocumentCount" : 463
                    }, 
                    {
                        "activityId" : "a8bf762a-37b9-4c07-8ed4-ae49961373c0",
                        "shardKeyRangeId" : "2",
                        "durationMS" : 35.8328,
                        "preemptions" : 1,
                        "outputDocumentCount" : 905,
                        "retrievedDocumentCount" : 905
                    }, 
                    {
                        "activityId" : "3754e36b-4258-49a6-8d4d-010555628395",
                        "shardKeyRangeId" : "1",
                        "durationMS" : 67.3969,
                        "preemptions" : 1,
                        "outputDocumentCount" : 1479,
                        "retrievedDocumentCount" : 1479
                    }, 
                    {
                        "activityId" : "a69a44ee-db97-4fe9-b489-3791f3d52878",
                        "shardKeyRangeId" : "0",
                        "durationMS" : 185.1523,
                        "preemptions" : 1,
                        "outputDocumentCount" : 867,
                        "retrievedDocumentCount" : 867
                    }
                ]

Оптимизации, сокращающие задержку запросов.

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

Добейтесь большей близости.

Запросы, которые выполняются из иного региона, чем тот, где находится учетная запись Azure Cosmos DB, будут иметь большую задержку, чем если бы они выполнялись в одном регионе. Например, если выполнять код на своем настольном компьютере, задержка повысится на десятки и сотни миллисекунд (если не больше), по сравнению с запросом, поступившим с виртуальной машины в том же регионе Azure, что и Azure Cosmos DB. Можно легко глобально распределить данные в Azure Cosmos DB, чтобы обеспечить близость данных к приложениям.

Увеличьте подготовленную пропускную способность.

В Azure Cosmos DB подготовленная пропускная способность измеряется в единицах запроса (ЕЗ). Представьте, что у вас есть запрос, использующий 5 ЕЗ пропускной способности. Например, если вы подготавливаете 1000 ЕЗ, вы сможете выполнить запрос 200 раз в секунду. Если вы попытались выполнить запрос при недостаточной пропускной способности, Azure Cosmos DB ограничит скорость запроса. API Azure Cosmos DB для MongoDB будет автоматически повторять этот запрос после ожидания в течение короткого времени. Регулируемые запросы занимают больше времени, поэтому увеличение подготовленной пропускной способности может улучшить задержку запросов.

Значение estimatedDelayFromRateLimitingInMilliseconds дает представление о возможных преимуществах задержки при увеличении пропускной способности.

Следующие шаги