Советы по повышению производительности для Azure Cosmos DB и .NET

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

Azure Cosmos DB — быстрая и гибкая распределенная база данных, которая легко масштабируется с гарантированными уровнями задержки и пропускной способности. Для масштабирования базы данных с помощью Azure Cosmos DB не нужно вносить в архитектуру существенные изменения или писать сложный код. Для увеличения или уменьшения масштаба достаточно выполнить один вызов API. Дополнительные сведения см. в разделах о подготовке пропускной способности контейнера и о подготовке пропускной способности базы данных.

Так как для доступа к Azure Cosmos DB выполняются сетевые вызовы, вы можете выполнить оптимизации на стороне клиента, чтобы повысить производительность при работе с пакетом SDK .NET для SQL.

Если вы хотите повысить производительность базы данных, рассмотрите варианты, описанные в следующих разделах.

Рекомендации по размещению

Включение сборки мусора на стороне сервера

В некоторых случаях для оптимизации можно уменьшить частоту сборки мусора. В .NET установите для параметра gcServer значение true.

Горизонтальное увеличение масштаба рабочей нагрузки клиента

При тестировании на высоких уровнях пропускной способности или скорости, превышающей 50 000 единиц запроса в секунду, клиентское приложение может стать узким местом рабочей нагрузки. Это связано с тем, что компьютер может превысить допустимую нагрузку при использовании ЦП или сети. Если вы достигли этой точки, то можете повысить производительность Azure Cosmos DB, развернув клиентские приложения на нескольких серверах.

Примечание.

Высокая загрузка ЦП может привести к увеличению задержек и к исключениям в связи с временем ожидания выполнения запросов.

Операции с метаданными

Не проверяйте существование базы данных и (или) контейнера с помощью вызова Create...IfNotExistsAsync и (или) Read...Async в критическом пути и (или) перед выполнением операции с элементом. Такая проверка должна выполняться только при запуске приложения, когда она необходима, то есть если вы ожидаете удаления объектов (в противном случае она не требуется). Эти операции с метаданными создают дополнительные задержки, не поддерживают Соглашения об уровне обслуживания и имеют собственные ограничения, которые плохо масштабируются, в отличие от операций с данными.

Отладка и трассировка

В некоторых средах включено средство .NET DefaultTraceListener. DefaultTraceListener создает проблемы с производительностью в рабочих средах, что приводит к возникновению узких мест из-за высокой загрузки ЦП и большого числа операций ввода-вывода. Проверьте и убедитесь, что средство DefaultTraceListener отключено для приложения, удалив его из коллекции TraceListeners в рабочих средах.

Последние версии пакета SDK (после 3.23.0) автоматически удаляют его при обнаружении в более старых версиях. Вы можете удалить его сами, как указано ниже.

if (!Debugger.IsAttached)
{
    Type defaultTrace = Type.GetType("Microsoft.Azure.Cosmos.Core.Trace.DefaultTrace,Microsoft.Azure.Cosmos.Direct");
    TraceSource traceSource = (TraceSource)defaultTrace.GetProperty("TraceSource").GetValue(null);
    traceSource.Listeners.Remove("Default");
    // Add your own trace listeners
}

Сеть

Политика подключения: использование режима прямого подключения

Для пакета SDK для .NET v3 по умолчанию используется прямое подключение по протоколу TCP. Режим подключения настраивается при создании экземпляра CosmosClient в CosmosClientOptions. Дополнительные сведения о различных вариантах подключения см. в статье Режимы подключения.

string connectionString = "<your-account-connection-string>";
CosmosClient client = new CosmosClient(connectionString,
new CosmosClientOptions
{
    ConnectionMode = ConnectionMode.Gateway // ConnectionMode.Direct is the default
});

Временная нехватка портов

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

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

В сценариях с разреженным доступом, если вы видите более высокое число подключений по сравнению с режимом шлюза, вы можете:

  • Задать для свойства CosmosClientOptions.PortReuseMode значение PrivatePortPool (действует с версиями платформы 4.6.1 и более поздними и версиями .NET Core 2.0 и более поздними). Это свойство позволяет пакету SDK использовать небольшой пул временных портов для разных конечных точек назначения Azure Cosmos DB.
  • Настройте свойство CosmosClientOptions.IdleTcp Подключение ionTimeout как больше или равно 10 минут. Рекомендуется использовать значения от 20 минут до 24 часов.

Повышение производительности за счет размещения клиентов в одном регионе Azure

Если это возможно, размещайте приложения, выполняющие вызовы к Azure Cosmos DB, в том же регионе, в котором находится база данных Azure Cosmos DB. Для приблизительного сравнения: вызовы к Azure Cosmos DB в пределах региона выполняются в течение 1–2 мс, но задержка между восточным и западным побережьем США превышает 50 мс. Значение задержки может отличаться в зависимости от выбранного маршрута при передаче запроса от клиента к границе центра обработки данных Azure.

Можно достичь минимальной задержки при размещении клиентского приложения в том же регионе Azure, в котором предоставляется конечная точка Azure Cosmos DB. Список доступных регионов см. на странице с регионами Azure.

Совместно разместите клиентов в одном регионе.

Увеличение количества потоков или задач

Так как вызовы Cosmos DB выполняются по сети, может потребоваться изменить степень параллелизма запросов, чтобы клиентское приложение тратило минимальное количество времени на ожидание между запросами. Например, если вы используете библиотеку параллельных задач .NET, создайте около нескольких сотен задач считывания или записи в Azure Cosmos DB.

Включение ускорения сети для уменьшения задержки и jitter ЦП

Рекомендуется следовать инструкциям, чтобы включить ускоренную сеть в Windows (щелкните инструкции) или Linux (щелкните инструкции) виртуальной машины Azure, чтобы повысить производительность.

Без ускоренной сети операции ввода-вывода, выполняемые между виртуальной машиной Azure и другими ресурсами Azure, могут без необходимости маршрутизироваться через узел и виртуальный коммутатор, расположенный между виртуальной машиной и ее сетевой картой. Наличие узла и виртуального коммутатора внутри пути данных не только увеличивает задержку и дрожание в коммуникационном канале, но также приводит к краже циклов ЦП у виртуальной машины. Благодаря ускоренной сети виртуальная машина взаимодействует напрямую с сетевой картой, без посредников; все сведения о политике сети, которые ранее обрабатывались узлом и виртуальным коммутатором, теперь обрабатываются оборудованием на сетевой карте, а узел и виртуальный коммутатор обходятся. При включении ускоренной сети, как правило, можно ожидать снижение задержки и увеличение пропускной способности, а также улучшение согласованности задержки и снижение загрузки ЦП.

Ограничения: ускоренная сеть должна поддерживаться в ОС виртуальной машины и может быть включена только при остановке и освобождении виртуальной машины. Виртуальную машину невозможно развернуть с помощью Azure Resource Manager. Служба приложений не включает ускоренную сеть.

Дополнительные сведения см. в инструкциях для Windows и Linux.

Использование пакета SDK

Установка последней версии пакета SDK

Пакеты SDK для Azure Cosmos DB постоянно улучшаются, чтобы обеспечивать самую высокую производительность. Сведения о последней версии пакета SDK и об улучшениях см. в статье о пакете SDK для Azure Cosmos DB.

Использование API-интерфейсов потока

Пакет SDK для .NET v3 содержит API-интерфейсы потока, которые могут получать и возвращать данные без сериализации.

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

Использование одного и того же клиента Azure Cosmos DB в течение всего жизненного цикла приложения

Каждый экземпляр CosmosClient в режиме прямого подключения является потокобезопасным, эффективно управляет подключениями и кэширует адреса. Чтобы обеспечить эффективное управление подключениями и более высокую производительность клиента пакета SDK, рекомендуется использовать один экземпляр за AppDomain время существования приложения для каждой учетной записи, с которыми взаимодействует ваше приложение.

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

При работе с Функциями Azure экземпляры также должны следовать существующим рекомендациям и поддерживать один экземпляр.

Избегайте блокирующих вызовов

Пакет SDK Для Azure Cosmos DB должен быть разработан для одновременной обработки множества запросов. Асинхронные API позволяют небольшому пулу потоков работать с тысячами одновременных запросов, не дожидаясь блокировки вызовов. Вместо ожидания завершения длительной синхронной задачи поток может работать с другим запросом.

Распространенная проблема с производительностью в приложениях с помощью пакета SDK Azure Cosmos DB блокирует вызовы, которые могут быть асинхронными. Множество синхронных вызовов блокировки может привести к истощению ресурсов пула потоков и увеличении времени отклика.

Не рекомендуется:

  • Блокировать асинхронное выполнение путем вызова Task.Wait или Task.Result.
  • Использовать Task.Run, чтобы сделать синхронный API асинхронным.
  • Получать блокировки в общих путях кода. Пакет SDK для .NET для Azure Cosmos DB является наиболее эффективной при разработке кода параллельно.
  • Вызывать Task.Run и сразу ожидать его. ASP.NET Core уже выполняет код приложения в обычных потоках пула потоков, поэтому вызов Task.Run приведет только к лишнему планированию пула потоков. Даже если запланированный код блокирует поток, Task.Run не препятствует этому.
  • Использовать ToList() в Container.GetItemLinqQueryable<T>(), в котором для синхронной очистки запроса используются блокирующие вызовы. Используйте ToFeedIterator() для асинхронной очистки запросов.

Сделайте следующее:

  • Асинхронно вызовите API .NET для Azure Cosmos DB.
  • Весь стек вызовов является асинхронным, чтобы использовать преимущества шаблонов async/await.

Можно использовать профилировщик, например PerfView, чтобы находить потоки, часто добавляемые в пул потоков. Событие Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start указывает поток, добавленный в пул потоков.

Отключение ответа содержимого при операциях записи

Для рабочих нагрузок, имеющих большое количество полезных данных, установите для параметра запроса EnableContentResponseOnWrite значение false . Служба больше не будет возвращать созданный или обновленный ресурс пакету SDK. Как правило, поскольку приложение имеет создаваемый объект, служба не должна его возвращать. Значения заголовков по-прежнему доступны, например плата за запрос. Отключение реагирования на содержимое может помочь повысить производительность, поскольку пакету SDK больше не требуется выделять память или сериализовать текст ответа. Это также сокращает использование пропускной способности сети для еще большего повышения производительности.

ItemRequestOptions requestOptions = new ItemRequestOptions() { EnableContentResponseOnWrite = false };
ItemResponse<Book> itemResponse = await this.container.CreateItemAsync<Book>(book, new PartitionKey(book.pk), requestOptions);
// Resource will be null
itemResponse.Resource

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

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

Увеличение максимального количества подключений System.Net для каждого узла при использовании режима шлюза

При использовании режима шлюза запросы Azure Cosmos DB выполняются по протоколу HTTPS/RESTFUL. К ним применяются ограничения на количество подключений по умолчанию для каждого имени узла или IP-адреса. Может потребоваться задать для параметра MaxConnections более высокое значение (от 100 до 1000), чтобы клиентская библиотека могла использовать несколько одновременных подключений к Azure Cosmos DB. В пакете SDK для .NET 1.8.0 и более поздних версиях значение по умолчанию для ServicePointManager.DefaultConnectionLimit равно 50. Чтобы изменить значение, можно задать для Documents.Client.ConnectionPolicy.MaxConnectionLimit более высокое значение.

Увеличение количества потоков или задач

См. подраздел Увеличение количества потоков или задач раздела "Сеть" этой статьи.

Операции запросов

Операции с запросами см. в разделе Рекомендации по повышению производительности запросов.

Политика индексирования

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

Политика индексирования Azure Cosmos DB также позволяет добавлять пути к документам или исключать их из индексирования. Для этого используется параметр Indexing Paths (IndexingPolicy.IncludedPaths и IndexingPolicy.ExcludedPaths).

Индексирование только нужных путей может повысить производительность операций записи, сократить расходы на единицу запроса при операциях записи и сократить объем хранилища индексов для сценариев, в которых эти шаблоны запросов известны заранее. Это обусловлено тем, что стоимость индексирования напрямую соотносится с количеством уникальных путей. Например, в коде ниже показано, как с помощью оператора подстановочного знака "*" исключить из индексации целый раздел документов (поддерево).

var containerProperties = new ContainerProperties(id: "excludedPathCollection", partitionKeyPath: "/pk" );
containerProperties.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
containerProperties.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/nonIndexedContent/*");
Container container = await this.cosmosDatabase.CreateContainerAsync(containerProperties);

Дополнительные сведения см. в статье Политики индексации Azure Cosmos DB.

Пропускная способность

Измерение и настройка для использования меньшего числа единиц запросов в секунду

В Azure Cosmos DB предлагается обширный набор операций базы данных. Эти операции включают реляционные и иерархические запросы с использованием файлов в универсальном дисковом формате (UDF), хранимых процедур и триггеров для документов в коллекции базы данных.

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

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

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

Для оценки расходов на каждую операцию (создание, обновление или удаление) посмотрите на заголовок x-ms-request-charge (или эквивалентное свойство RequestCharge в ResourceResponse<T> или FeedResponse<T> в пакете SDK для .NET), чтобы измерить число единиц запроса, используемых такими операциями.

// Measure the performance (Request Units) of writes
ItemResponse<Book> response = await container.CreateItemAsync<Book>(myBook, new PartitionKey(myBook.PkValue));
Console.WriteLine("Insert of item consumed {0} request units", response.RequestCharge);
// Measure the performance (Request Units) of queries
FeedIterator<Book> queryable = container.GetItemQueryIterator<ToDoActivity>(queryString);
while (queryable.HasMoreResults)
    {
        FeedResponse<Book> queryResponse = await queryable.ExecuteNextAsync<Book>();
        Console.WriteLine("Query batch consumed {0} request units", queryResponse.RequestCharge);
    }

Затраты на запрос, возвращенные в этом заголовке, — это часть подготовленной пропускной способности (например, 2000 единиц запросов в секунду). Например, если приведенный выше запрос вернет 1000 документов размером по 1 КБ каждый, затраты на операцию составят 1000. Таким образом, перед ограничением частоты выполнения последующих запросов сервер за одну секунду выполняет лишь два таких запроса. Чтобы узнать больше, ознакомьтесь с единицами запроса и калькулятором единиц запроса.

Обработка ограничения скорости / слишком высокая частота запросов

Выполнение запроса, который пытается превысить лимит зарезервированной пропускной способности для учетной записи, не приводит к замедлению работы сервера, так как пользователь не сможет превысить это зарезервированное значение. Сервер заблаговременно завершает запрос с ошибкой RequestRateTooLarge (код статуса HTTP 429). Он возвращает заголовок x-ms-retry-after-ms, который указывает время ожидания (в миллисекундах), по истечении которого пользователь должен выполнить запрос еще раз.

    HTTP Status 429,
    Status Line: RequestRateTooLarge
    x-ms-retry-after-ms :100

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

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

Можно изменить число повторных попыток по умолчанию, задав RetryOptions для экземпляра CosmosClientOptions. По умолчанию в случае превышения заданного счетчика повторов исключение CosmosException с кодом состояния 429 возвращается через 30 секунд (совокупное время ожидания). Эта ошибка возвращается, даже если текущее число повторных попыток меньше максимального, независимо от того, является ли текущее значение значением по умолчанию 9 или значением, определенным пользователем.

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

Дополнительные сведения см. в статье Единицы запроса.

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

Плата за запрос (т. е. затраты на обработку запросов) указанной операции напрямую коррелирует с размером документа. За операции с большими документами взимается больше единиц запроса, чем за операции с мелкими документами.

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

Пример приложения, используемого в сценариях оценки производительности Azure Cosmos DB на нескольких клиентских компьютерах, см. в статье Проверка производительности и масштабирования с помощью Azure Cosmos DB.

Дополнительные сведения о создании приложения с высокой масштабируемостью и производительностью см. в статье Partitioning and scaling in Azure Cosmos DB (Секционирование и масштабирование в Azure Cosmos DB).