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

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

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

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

Обновление до пакета SDK для .NET версии 3.

Пакет SDK для .NET версии 3 выпущен. Если вы используете пакет SDK для .NET версии 3, ознакомьтесь с руководством по производительности .NET версии 3 для получения следующих сведений:

  • значения по умолчанию для режима прямого TCP;
  • поддержка API потока;
  • поддержка пользовательского сериализатора для использования System.Text.JSON;
  • интегрированная поддержка пакетов и пакетных операций.

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

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

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

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

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

Примечание.

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

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

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

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

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

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

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

Сеть

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

По умолчанию для пакета SDK для .NET версии 2 используется режим подключения через шлюз. Режим подключения настраивается во время создания экземпляра DocumentClient с помощью параметра ConnectionPolicy. При использовании режима прямого подключения необходимо также задать Protocol с помощью параметра ConnectionPolicy. Дополнительные сведения о различных вариантах подключения см. в статье Режимы подключения.

Uri serviceEndpoint = new Uri("https://contoso.documents.net");
string authKey = "your authKey from the Azure portal";
DocumentClient client = new DocumentClient(serviceEndpoint, authKey,
new ConnectionPolicy
{
   ConnectionMode = ConnectionMode.Direct, // ConnectionMode.Gateway is the default
   ConnectionProtocol = Protocol.Tcp
});

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

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

При запуске по протоколу TCP клиент оптимизирует задержку, используя длительные подключения, в отличие от протокола HTTPS, который прерывает подключения после 2 минут бездействия.

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

  • Задать для свойства ConnectionPolicy.PortReuseMode значение PrivatePortPool (действует в версии платформы >= 4.6.1 и версии .NET >= 2.0). Это свойство позволяет пакету SDK использовать небольшой пул временных портов для разных конечных точек назначения Cosmos DB.
  • Задать для свойства ConnectionPolicy.IdleConnectionTimeout значение, которое больше или равно 10 минутам. Рекомендуется использовать значения от 20 минут до 24 часов.

Использование вызовов OpenAsync для избежания задержки при выполнении первого запроса

По умолчанию первый запрос характеризуется более длительной задержкой, так как он должен получить таблицу маршрутизации сетевых адресов. При использовании пакета SDK версии 2 вызовите OpenAsync() один раз во время инициализации, чтобы избежать задержки запуска при первом запросе. Вызов выглядит следующим образом: await client.OpenAsync();

Примечание.

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

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

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

Политика подключения к Azure Cosmos DB

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

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

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

Чтобы сократить задержку и нагрузку на ЦП, рекомендуется включить ускорение сети на клиентских виртуальных машинах. Дополнительные сведения см. в статьях Создание виртуальной машины Windows с ускорением сети и Создание виртуальной машины Linux с ускорением сети.

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

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

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

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

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

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

Пакет 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(), в DocumentClient.CreateDocumentQuery(...) котором используются блокирующие вызовы для синхронного очистки запроса. Используйте AsDocumentQuery() для асинхронной очистки запросов.

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

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

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

Увеличение максимального количества подключений 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 более высокое значение.

Реализация интервала задержки для RetryAfter

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

Поддержка политики повтора включена в следующие пакеты SDK:

Дополнительные сведения см. в статье RetryAfter.

В версии 1.19 и последующих версиях пакета SDK для .NET появился механизм для регистрации дополнительных диагностических сведений и устранения проблем, связанных с задержкой, как показано в следующем примере. Вы можете зарегистрировать строку диагностики для запросов с высокой задержкой чтения. Захваченная строка диагностики поможет понять, сколько раз была получена ошибка 429 для данного запроса.

ResourceResponse<Document> readDocument = await this.readClient.ReadDocumentAsync(oldDocuments[i].SelfLink);
readDocument.RequestDiagnosticsString 

Кэширование URI документов для снижения задержки чтения

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

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

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

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

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

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

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

Политика индексирования Azure Cosmos DB также позволяет добавлять пути к документам или исключать их из индексирования. Для этого используется параметр Indexing Paths (IndexingPolicy.IncludedPaths и IndexingPolicy.ExcludedPaths). Пути индексирования могут обеспечить повышение производительности при записи и понижение требований к хранилищу индекса в сценариях, когда шаблоны запросов известны заранее. Это обусловлено тем, что стоимость индексирования напрямую соотносится с количеством уникальных путей. Например, в коде ниже показано, как с помощью оператора подстановочного знака "*" исключить из индексации целый раздел документов (поддерево):

var collection = new DocumentCollection { Id = "excludedPathCollection" };
collection.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
collection.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/nonIndexedContent/*");
collection = await client.CreateDocumentCollectionAsync(UriFactory.CreateDatabaseUri("db"), collection);

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

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

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

В Azure Cosmos DB предлагается обширный набор операций базы данных. Эти операции включают реляционные и иерархические запросы с использованием UDF, хранимых процедур и триггеров для документов в коллекции базы данных. Затраты, связанные с каждой из этих операций, зависят от типа процессора, операций ввода-вывода и памяти, необходимой для завершения операции. Вместо планирования и управления ресурсами оборудования можно использовать такое понятие как "единица запроса" (Request Unit, RU), представляющее собой единицу измерения ресурсов, необходимых для выполнения различных операций базы данных и обслуживания запросов приложений.

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

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

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

// Measure the performance (Request Units) of writes
ResourceResponse<Document> response = await client.CreateDocumentAsync(collectionSelfLink, myDocument);
Console.WriteLine("Insert of document consumed {0} request units", response.RequestCharge);
// Measure the performance (Request Units) of queries
IDocumentQuery<dynamic> queryable = client.CreateDocumentQuery(collectionSelfLink, queryString).AsDocumentQuery();
while (queryable.HasMoreResults)
    {
        FeedResponse<dynamic> queryResponse = await queryable.ExecuteNextAsync<dynamic>();
        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, может быть недостаточным. В этом случае клиент создает в приложении исключение DocumentClientException с кодом состояния 429.

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

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

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

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

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

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

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