Как выполнять быструю (более) агрегацию большого количества записей в Cosmos DB?

В настоящее время у меня есть документы, моделирующие электронные письма, которые немного похожи на следующие

{
    "AccountId": "AccountId",
    "Brand": "MyBrand",
    "Product": "MyProduct",
    "Metadata": {
        "Campaign": "EmailCampaign1",
        "Metadata2": "Some other info",
    },
    "Status": {
        "State": "delivered",
        "DeliveryEvents": [
            {
                "Event": "delivered",
                "DateTimeOccured": "2019-03-14T12:25:12Z",
            },
            {
                "Event": "processed",
                "DateTimeOccured": "2019-03-14T12:25:09Z"
            }
        ]
    },
    "id": "AnId",
    "CreatedAt": 1552566306,
    "Stats": {
        "DeliveryStats": {
            "processed": true,
            "deferred": false,
            "delivered": true,
            "dropped": false,
            "bounce": false
        }
    }
}

Для справки, AccountId в настоящее время является ключом раздела.

И я хотел сделать COUNT на DeliveryStats, где вы могли бы фильтровать по одному или нескольким из следующих:

  • AccountId
  • Brand
  • Metadata (поиск пары ключ-значение)
  • CreatedAt (например, между двумя датами).

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

SELECT VALUE COUNT(1) FROM c WHERE c.Stats.DeliveryStats.processed = true AND c.Brand = 'MyBrand' AND c.Metadata.Campaign = 'EmailCampaign1'

Все запрашиваемое индексируется.

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

Мой вопрос в том, правильно ли написан этот запрос? Есть ли что-нибудь еще, что я могу сделать, чтобы ускорить этот запрос?

Открыты для реструктуризации данных или хранения дополнительных данных.


person Daniel Edwards    schedule 15.03.2019    source источник


Ответы (1)


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

Индивидуальная индексная селективность

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

Если это так, вы можете рассмотреть возможность объединения значений pf по отдельности, недостаточно селективных значений столбцов, в одно свойство с хэш-индексом для поиска. Бывший:

{
    "AccountId": "AccountId",
    "Brand": "MyBrand",
    "Metadata": {
        "Campaign": "EmailCampaign1",
    },
    ...
    "MergedForLookup": "MyBrand_EmailCampaign_processed"
    ...
}

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

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

Межраздельный запрос

Следующий кандидат связан с тем фактом, что вы выполняете запрос между разделами. В основном запрос на каждый раздел = N запросов. Если ваши данные вырастут до миллионов, то, скорее всего, у них будет много разделов (= идентификаторы учетных записей) + ваши данные будут разделены на несколько физических разделов внутри, и это определенно окажет влияние. Если возможно, вам следует проверить, улучшит ли ситуацию включение фильтра AccountId. Если возможно, сделайте фильтр "AccountId" обязательным.

Эффективно ли count() использует индекс

Если запрос выполняется медленнее, чем вы ожидаете, даже при запросе с одним разделом, то есть сообщения о том, что count() запросы не так эффективны, как можно было бы ожидать. Проверьте следующую проблему и проголосуйте: "Уведомление COUNT() об индексах" в отзывах CosmosDB.

.. количество всех различных DeliveryStats.

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

Прямо сейчас, если есть относительно мало фиксированных значений, просто выполните эти 5 или сколько угодно запросов для каждой статистики последовательно. Если каждый из них правильно использует раздел/индекс, то он все равно должен быть молниеносным (если быть точным, в 5 раз молниеноснее: P).

person Imre Pühvel    schedule 19.03.2019