Microsoft Azure Cosmos DocumentDB Оптимальная производительность запросов чтения - PullRequest
0 голосов
/ 31 августа 2018

Мы внедрили базу данных Azure CosmosDB (MongoDB с SQL API) в облаке. С помощью Java мы хотели бы создавать отчеты на основе данных, скрытых в MongoDB. Я еще не очень доволен производительностью моих запросов на чтение, и мне было интересно, что можно улучшить в моей текущей настройке.

Как уже говорилось, я использую Java для запросов к базе данных. Я использую библиотеку Microsoft Azure DocumentDB для запроса базы данных:

<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-documentdb</artifactId>
    <version>1.16.2</version>
</dependency>

В настоящее время лучшая производительность, которую я смог получить, - это запрос около 38 000 документов в памяти за 20 секунд с настроенной скоростью 50 000 RU / s (локальный эмулятор космоса). Мне бы очень хотелось, чтобы это улучшилось, потому что мы могли бы вскоре запросить миллионы документов.

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

{
    "deviceid": "xxx",
    "devicedata": {
        "datetime": "2018-08-28T00:00:02.104Z",
        "sensors": [
            {
                "p_A2": "93095",
                "p_A3": "303883",
                "p_batterycurrent": "4294967.10000",
                "p_batterygauge": "38.27700",
                "p_batteryvoltage": "13.59400",
                ** ... around 200 more key - value pairs ... **
            }
        ]
    },
    "id": "aa5d3cf5-10fa-48dd-a0d2-a536284eddac",
    "_rid": "PtEIANkbMQABAAAAAAAAAA==",
    "_self": "dbs/PtEIAA==/colls/PtEIANkbMQA=/docs/PtEIANkbMQABAAAAAAAAAA==/",
    "_etag": "\"00000000-0000-0000-4040-006a7f2501d4\"",
    "_attachments": "attachments/",
    "_ts": 1535619672
}

Запрос, который мы будем часто использовать, будет выглядеть следующим образом:

SELECT c.deviceid, 
    c.devicedata.datetime, 
    c.devicedata.sensors[0].p_A2, 
    c.devicedata.sensors[0].p_A3,
    c.devicedata.sensors[0].p_batterycurrent,
    c.devicedata.sensors[0].s_humidity 
FROM c 
WHERE c.deviceid = 'xxx'
    AND c.devicedata.datetime >= '2018-08-28T00:00:00.000Z' 
    AND c.devicedata.datetime < '2018-08-30T00:00:00.000Z' 
order by c.devicedata.datetime desc

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

Такой запрос, как указано выше, займет у нас около 20 секунд.

Однако я заметил, что если я запрашиваю только deviceid и devicedata.datetime, запрос выполняется в течение 2 секунд. Кажется, что вытащить данные датчика из списка датчиков - действительно сложная задача. Если я выбираю * (то есть не фильтрую данные датчика), это также быстрее, чем когда я позволяю SQL API отфильтровывать датчики: около 15 секунд.

Мой вопрос: что я могу сделать, чтобы улучшить это? Мой список документов слишком длинный? Есть ли способ, которым я могу настроить это по-другому? Пары значений ключа датчика не являются фиксированными и могут отличаться для разных устройств.

Еще несколько технических деталей: У меня есть неограниченная коллекция, разбитая на / deviceid. Я использовал стандартную политику индексации Azure (которая индексирует все), а также исключил из нее датчики.

Я попробовал все советы, как описано здесь: https://docs.microsoft.com/en-us/azure/cosmos-db/performance-tips-java

Это моя текущая настройка Java, хотя я пробовал много разных вещей:

//This piece of code is currently in a seperate thread. There is one thread per deviceId to query
documentClient = new DocumentClient(HOST, MASTER_KEY,
                 ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);

FeedOptions options = new FeedOptions();
options.setEnableCrossPartitionQuery(true);

documentList = documentClient
    .queryDocuments(getAlldataCollection().getSelfLink(), query, options)
    .getQueryIterable().toList();

Я вполне уверен, что MongoDB может запросить сотни тысяч документов в течение нескольких секунд, поэтому я почти уверен, что что-то не так с моей текущей настройкой.

Есть предложения?

Ответы [ 2 ]

0 голосов
/ 10 сентября 2018

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

Когда вы запускаете команду select *, запрос просто возвращает весь BLOB-объект, поэтому анализ не требуется.

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

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

Два, уберите порядок на, это еще больше уменьшит обработку на стороне сервера.

0 голосов
/ 10 сентября 2018

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

Нет. Подходит ли?

Во-первых, чтобы убедиться в этом, вы уверены, что ваш сценарий хорошо подходит для noSQL? CosmosDB светится, когда основной сценарий работает с точными данными (создание, выбор по идентификатору, обновление по идентификатору, удаление по идентификатору). Да, он определенно может выполнять ограниченные массовые операции и агрегации, но запрос миллионов требует его. С другой стороны, SQL предназначен для работы с большими наборами данных и действительно хорош для агрегирования.

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

Отладка для сложных данных

Не выполнять тесты производительности на локальном эмуляторе cosmosDB . Не. Это, очевидно, не реальная вещь (учитывая сеть, пропускную способность хранилища / время поиска, влияние системы), но только эмулирует это. Вы можете получить очень вводящие в заблуждение результаты. Раскрути реальный тестовый экземпляр .

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

Кроме того, загрузка 38000 документов, скорее всего, никогда не поступит в виде одного пакета, проверьте, сколько фактически запросов продолжения выполняется на сервер cosmosDB.

Кроме того, запустите профилировщик и убедитесь, что узкое место действительно в CosmosDB. Если вы делаете много продолжительных вызовов И одновременно запрашиваете на многих устройствах, то это может часто происходить и в клиенте, и запросы летают в сети. Убедитесь, что вы не ограничены в клиенте (GC, стек Http, внутренняя блокировка, пулы соединений / потоков и т. Д.).

Data / Query design

Сокращение запрашиваемых данных

Если вы уже знаете deviceid, то не запрашивайте его 38000+ раз - это просто балласт.

Уменьшить размер объекта модели

/ * еще около 200 пар ключ-значение * /

Это огромный объект. Я бы проверил, поможет ли разделение на более мелкие объекты cosmosDB тратить меньше времени на внутреннюю загрузку и обработку документов. Пример:

{
    "p_A2": "93095",
    "p_A3": "303883",
    "battery" : {
        "current": "4294967.10000",
        "gauge": "38.27700",
        "voltage": "13.59400"
    }
   ...
}

Не уверен, как docDB хранит документы внутри себя (полный график против вложенных документов), но вы можете проверить, оказывает ли это влияние. Разница между 2 против 20 настолько велика, что намекает на то, что она может иметь значение.

Массив датчиков?

Запрос запрашивает только первый первый набор измерений. Нужен ли массив? Вы можете проверить, не влияет ли этот уровень на производительность.

Типы данных в модели

battery_current и т. Д. Хранят числовые значения измерений датчика в виде длинных строк. Если они всегда являются числами, вы можете хранить их как числа и уменьшить размер документа на сервере и клиенте. На производительность клиента, вероятно, повлияло бы больше (строка = выделение кучи). Пример: "4294967.10000" составляет 13 символов = 26B в клиенте (UTF-16).

Дизайн приложения

Тебе действительно нужны все эти 38000 или миллионы документов каждый раз? Подумайте, можете ли вы обойтись подмножеством ..

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

Как всегда, рассмотрите ваше экономическое обоснование для ROI . Оптимизация всегда возможна, но иногда выгоднее скорректировать бизнес-требования, чем технические решения.

...