Sugerencias de rendimiento para Azure Cosmos DB y .NET

SE APLICA A: NoSQL

Azure Cosmos DB es una base de datos distribuida, rápida y flexible que se escala sin problemas con niveles de latencia y rendimiento garantizados. No es necesario realizar cambios de arquitectura importantes ni escribir el código complejo para escalar la base de datos con Azure Cosmos DB. Escalar o reducir verticalmente es tan sencillo como realizar una única llamada API. Para más información, consulte Aprovisionamiento del rendimiento del contenedor o Aprovisionamiento del rendimiento de la base de datos.

Dado que se accede a Azure Cosmos DB mediante llamadas de red, puede realizar optimizaciones del lado cliente para lograr el máximo rendimiento cuando se usa el SDK de SQL para .NET.

Si intenta mejorar el rendimiento de la base de datos, tenga en cuenta las opciones que se muestran en las secciones siguientes.

Hospedando recomendaciones

Activación de la recolección de elementos no utilizados del lado servidor

La reducción de la frecuencia de recolección de elementos no utilizados puede ayudar en algunos casos. En .NET, establezca gcServer en true.

Escalado horizontal de la carga de trabajo de cliente

Si va a realizar pruebas en niveles de rendimiento elevados o en velocidades superiores a 50 000 unidades de solicitud por segundo (RU/s), la aplicación cliente podría convertirse en un cuello de botella de la carga de trabajo. Esto se debe a que la máquina podría agotar el uso de la CPU o la red. Si llega a este punto, puede seguir insertando la cuenta de Azure Cosmos DB mediante la escala horizontal de las aplicaciones cliente en varios servidores.

Nota

Un uso elevado de CPU puede provocar el aumento de la latencia, así como excepciones de tiempo de espera de solicitud.

Operaciones de metadatos

No compruebe que existe una base de datos o un contenedor mediante una llamada a Create...IfNotExistsAsync o Read...Async en la ruta de acceso activa o antes de realizar una operación de elemento. La validación solo debe realizarse en el inicio de la aplicación cuando sea necesario, si espera que se eliminen (de lo contrario, no es necesario). Estas operaciones de metadatos generarán latencia adicional de un extremo a otro, no tendrán ningún Acuerdo de Nivel de Servicio y sus propias limitaciones independientes que no se escalan como las operaciones de datos.

Registro y seguimiento

Algunos entornos tienen habilitado .NET DefaultTraceListener. DefaultTraceListener plantea problemas de rendimiento en entornos de producción que provocan cuellos de botella altos de CPU y E/S. Compruebe y asegúrese de que DefaultTraceListener está deshabilitado para la aplicación; para ello, quítelo de TraceListeners en entornos de producción.

Las últimas versiones del SDK (superiores a 3.23.0) lo quitan automáticamente cuando lo detectan; con versiones anteriores, puede quitarlo mediante:

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
}

Redes

Directiva de conexión: uso del modo de conexión directa

El modo de conexión predeterminado del SDK de .NET V3 es directo con el protocolo TCP. El modo de conexión se configura cuando se crea la instancia de CosmosClient en CosmosClientOptions. Para obtener más información sobre las distintas opciones de conectividad, vea el artículo Modos de conectividad.

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

Agotamiento de puertos efímeros

Si en las instancias detecta un volumen de conexiones alto o un uso elevado de los puertos, compruebe primero que las instancias del cliente son singleton. Es decir, las instancias de cliente deben ser únicas para la duración de la aplicación.

Cuando se ejecuta en el protocolo TCP, el cliente se optimiza para la latencia mediante el uso de conexiones de larga duración. Esto contrasta con el protocolo HTTPS, que finaliza las conexiones después de dos minutos de inactividad.

En escenarios en los que tiene acceso disperso y observa un recuento de conexiones superior en comparación con el acceso del modo de puerta de enlace, puede hacer lo siguiente:

  • Configure la propiedad CosmosClientOptions.PortReuseMode en PrivatePortPool (válida con las versiones 4.6.1 y posteriores del marco y las versiones 2.0 y posteriores de .NET Core). Esta propiedad permite al SDK usar un pequeño grupo de puertos efímeros para varios puntos de conexión de destino de Azure Cosmos DB.
  • Configure la propiedad CosmosClientOptions.IdleTcpConnectionTimeout para que sea mayor o igual que 10 minutos. Los valores recomendados van de 20 minutos a 24 horas.

Para el rendimiento, colocar a los clientes en la misma región de Azure

Cuando sea posible, coloque las aplicaciones que llaman a Azure Cosmos DB en la misma región que la base de datos de Azure Cosmos DB. Esta es una comparación aproximada: las llamadas a Azure Cosmos DB en la misma región se completan entre 1 milisegundo (ms) y 2 ms, pero la latencia entre la costa oeste y la costa este de EE. UU. es superior a 50 ms. Esta latencia puede variar de una solicitud a otra, en función de la ruta realizada por la solicitud a medida que pasa del cliente al límite del centro de recursos de Azure.

Para obtener la menor latencia posible, asegúrese de que la aplicación que realiza la llamada se encuentra dentro de la misma región de Azure que el punto de conexión de Azure Cosmos DB aprovisionado. Para obtener una lista de las regiones disponibles, vea Regiones de Azure.

Colocar los clientes en la misma región.

Aumentar el número de subprocesos o tareas

Como las llamadas a Azure Cosmos DB se realizan a través de la red, es posible que tenga que cambiar el grado de simultaneidad de las solicitudes para que la aplicación cliente dedique un tiempo mínimo a esperar entre solicitudes. Por ejemplo, si usa la biblioteca TPL de .NET, cree en el orden de cientos de tareas que leen o escriben en Azure Cosmos DB.

Habilitación de redes aceleradas para reducir la latencia y la vibración de la CPU

Se recomienda seguir las instrucciones para habilitar redes aceleradas en su VM de Azure con Windows (haga clic para obtener instrucciones) o Linux (haga clic en las instrucciones) para maximizar el rendimiento.

Sin las redes aceleradas, la E/S que pasa entre la máquina virtual de Azure y otros recursos de Azure puede enrutarse innecesariamente a través de un host y un conmutador virtual situado entre la máquina virtual y su tarjeta de red. Si el host y el conmutador virtual están alineados en la ruta de datos no solo se aumenta la latencia y la vibración en el canal de comunicación, sino que también se roban ciclos de la CPU de la máquina virtual. Con las redes aceleradas, la máquina virtual interactúa directamente con la NIC sin intermediarios; los detalles de la directiva de red que se administraban mediante el host y el conmutador virtual se administran ahora en hardware en la NIC; se omiten el host y el conmutador virtual. Por lo general, al habilitar las redes aceleradas puede esperar una menor latencia y un mayor rendimiento, así como una latencia más uniforme y una disminución del uso de la CPU.

Limitaciones: las redes aceleradas deben ser compatibles con el sistema operativo de la máquina virtual y solo se pueden habilitar cuando la máquina virtual se ha detenido y se ha desasignado. No se puede implementar la máquina virtual con Azure Resource Manager. App Service no tiene habilitada la red acelerada.

Consulte las instrucciones de Windows y Linux para obtener más detalles.

Uso del SDK

Instalación del SDK más reciente

Los SDK de Azure Cosmos DB se mejoran constantemente para proporcionar el mejor rendimiento. Consulte SDK de Azure Cosmos DB para determinar las mejoras de los SDK y las revisiones más recientes.

Uso de las API de transmisión

El SDK de .NET V3 contiene API de secuencia que puede recibir y devolver datos sin serialización.

Las aplicaciones de nivel intermedio que no consumen respuestas directamente del SDK pero las transmiten a otros niveles de aplicaciones pueden beneficiarse de las API de transmisión. Para obtener ejemplos de control de la transmisión, consulte los ejemplos de Administración de elementos.

Uso de un cliente de Azure Cosmos DB singleton para aumentar la duración de la aplicación

Cada instancia de CosmosClient es segura para subprocesos y realiza una administración eficaz de la conexión y el almacenamiento en caché de direcciones cuando funciona en modo directo. Para permitir una administración de conexión eficaz y un mejor rendimiento del cliente de SDK, se recomienda usar una sola instancia por AppDomain durante la vigencia de la aplicación para cada cuenta con la que su aplicación interactúe.

Para las aplicaciones multiinquilino que controlan varias cuentas, consulte los procedimientos recomendados relacionados.

Al trabajar en Azure Functions, las instancias también deben seguir las instrucciones existentes y mantener una sola instancia.

Evitar llamadas de bloqueo

El SDK de Azure Cosmos DB debe estar diseñado para procesar muchas solicitudes a la vez. Las API asincrónicas permiten que un pequeño grupo de subprocesos controle miles de solicitudes simultáneas sin esperar a las llamadas de bloqueo. En lugar de esperar a que se complete una tarea sincrónica de larga duración, el subproceso puede trabajar en otra solicitud.

Un problema de rendimiento común en las aplicaciones que usan el SDK de Azure Cosmos DB es que las llamadas de bloqueo podrían ser asincrónicas. Muchas llamadas de bloqueo sincrónicas conducen a la escasez del grupo de subprocesos y a tiempos de respuesta degradados.

No:

  • Bloquee la ejecución asincrónica mediante la llamada a Task.Wait o Task.Result.
  • Use Task.Run para que una API sincrónica sea asincrónica.
  • Adquiera bloqueos en rutas de acceso de código comunes. El SDK de Azure Cosmos DB tiene el mejor rendimiento cuando se diseña para ejecutar código en paralelo.
  • Llame a Task.Run y espere inmediatamente. ASP.NET Core ya ejecuta código de aplicación en los subprocesos normales del grupo de subprocesos, por lo que la llamada a Task.Run solo da lugar a la programación adicional innecesaria del grupo de subprocesos. Aunque el código programado bloquee un subproceso, Task.Run no lo impide.
  • No use ToList() en Container.GetItemLinqQueryable<T>() si se usan llamadas de bloqueo para purgar la consulta de manera sincrónica. Use ToFeedIterator() para purgar la consulta de manera asincrónica.

:

  • Llame a las API de Azure Cosmos DB de manera asincrónica.
  • Toda la pila de llamadas es asincrónica para beneficiarse de los patrones async/await.

Un generador de perfiles, como PerfView, se puede usar para buscar subprocesos que se agregan con frecuencia al grupo de subprocesos. El evento Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start indica que se agregó un subproceso al grupo de subprocesos.

Deshabilitar la respuesta de contenido en operaciones de escritura

En el caso de cargas de trabajo con cargas útiles de operaciones de creación intensivas, establezca la opción de solicitud EnableContentResponseOnWrite en false. El servicio ya no devolverá al SDK el recurso creado o actualizado. Normalmente, dado que la aplicación tiene el objeto que se crea, no necesita que el servicio lo devuelva. Todavía se puede acceder a los valores de encabezado, como un cargo de solicitud. Deshabilitar el contenido de la respuesta puede ayudar a mejorar el rendimiento, porque el SDK ya no tendrá que asignar memoria ni serializar el cuerpo de la respuesta. También reduce el uso de ancho de banda de red para mejorar más el rendimiento.

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

Habilitación del modo masivo para optimizar el rendimiento en lugar de la latencia

Habilite el modo masivo para escenarios en los que la carga de trabajo requiere una gran cantidad de rendimiento y la latencia no es tan importante. Consulte Introducción al modo masivo para más información sobre cómo habilitar la característica y los escenarios en los que se debe usar.

Aumento de MaxConnectionsde System.Net por host al usar el modo de puerta de enlace

Las solicitudes de Azure Cosmos DB se realizan mediante HTTPS o REST cuando se usa el modo de puerta de enlace. Están sujetas al límite de conexiones predeterminado por nombre de host o dirección IP. Es posible que tenga que establecer MaxConnections en un valor superior (de 100 a 1000) para que la biblioteca cliente pueda utilizar varias conexiones simultáneas con Azure Cosmos DB. En el SDK de .NET 1.8.0 y versiones posteriores, el valor predeterminado de ServicePointManager.DefaultConnectionLimit es 50. Para cambiar el valor, puede establecer Documents.Client.ConnectionPolicy.MaxConnectionLimit en un valor superior.

Aumentar el número de subprocesos o tareas

Consulte Aumentar el número de subprocesos y tareas en la sección redes de este artículo.

Operaciones de consulta

Para las operaciones de consulta, consulte las sugerencias de rendimiento para las consultas.

Directiva de indexación

Exclusión de rutas de acceso sin utilizar de la indexación para acelerar las escrituras

La directiva de indexación de Azure Cosmos DB también le permite especificar las rutas de acceso de documentos que se van a incluir o excluir de la indexación mediante rutas de acceso de indexación (IndexingPolicy.IncludedPaths e IndexingPolicy.ExcludedPaths).

Si solo se indexan las rutas de acceso necesarias se puede mejorar el rendimiento de escritura, reducir el cargo de RU en las operaciones de escritura y reducir el almacenamiento de índices en los escenarios en los que los patrones de consulta se conocen con antelación. Esto se debe a que los costos de indización se relacionan directamente con el número de rutas de acceso únicas indizadas. Por ejemplo, en el siguiente código se muestra cómo excluir una sección completa de los documentos (un subárbol) de la indexación mediante el carácter comodín "*":

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);

Para más información, consulte Directivas de indexación de Azure Cosmos DB.

Throughput

Medir y optimizar para un uso inferior de RU/s

Azure Cosmos DB ofrece un amplio conjunto de operaciones de base de datos. Estas operaciones incluyen consultas relacionales y jerárquicas con archivos en formato de disco universal (UDF), procedimientos almacenados y desencadenadores que operan en los documentos de una colección de base de datos.

El costo asociado a cada una de estas operaciones varía en función de la CPU, la E/S y la memoria necesarias para completar la operación. En lugar de pensar en los recursos de hardware y cómo administrarlos, puede considerar una unidad de solicitud como una medida única de los recursos necesarios para realizar varias operaciones de base de datos y dar servicio a una solicitud de la aplicación.

El rendimiento se aprovisiona en función del número de unidades de solicitud establecidas para cada contenedor. El consumo de las unidades de solicitud se evalúa por segundos. Las aplicaciones que superan la frecuencia de unidad de solicitud aprovisionada para su contenedor se limitan hasta que la velocidad cae por debajo del nivel aprovisionado para el contenedor. Si su aplicación requiere un mayor nivel de rendimiento, puede aumentar el rendimiento mediante el aprovisionamiento de unidades de solicitud adicionales.

La complejidad de una consulta afecta a la cantidad de unidades de solicitud que se consumen para una operación. El número de predicados, la naturaleza de los predicados, el número de archivos UDF y el tamaño del conjunto de datos de origen influyen en el costo de las operaciones de consulta.

Para medir la sobrecarga de cualquier operación (crear, actualizar o eliminar), inspeccione el encabezado x-ms-request-charge (o la propiedad equivalente a RequestCharge en ResourceResponse<T> o FeedResponse<T> en el SDK de .NET) para medir el número de unidades de solicitud consumidas por las operaciones:

// 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);
    }

El cargo de solicitud devuelto en este encabezado es una fracción del rendimiento aprovisionado (es decir, 2000 RU/s). Por ejemplo, si la consulta anterior devuelve 1 000 documentos de 1 KB, el costo de la operación es 1 000. Por lo tanto, en un segundo, el servidor respeta solo dos solicitudes de este tipo antes de la limitación de velocidad de las solicitudes posteriores. Para más información, consulte Unidades de solicitud y la Calculadora de unidades de solicitud .

Administración de la limitación de velocidad y la tasa de solicitudes demasiado grande

Cuando un cliente intenta superar la capacidad de proceso reservada para una cuenta, no hay degradación del rendimiento en el servidor y no se usa la capacidad de rendimiento más allá del nivel reservado. El servidor finaliza de forma preventiva la solicitud con RequestRateTooLarge (código de estado HTTP 429). Devuelve un encabezado x-ms-retry-after-ms que indica la cantidad de tiempo, en milisegundos, que el usuario debe esperar antes de volver a intentar la solicitud.

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

Los SDK capturan implícitamente esta respuesta, respetan el encabezado retry-after especificado por el servidor y reintentan la solicitud. A menos que varios clientes obtengan acceso a la cuenta al mismo tiempo, el siguiente reintento se realizará correctamente.

Si tiene más de un cliente que funciona acumulativamente de forma sistemática por encima de la tasa de solicitudes, es posible que el número de reintentos predeterminado establecido en 9 internamente por el cliente no sea suficiente. En este caso, el cliente inicia una excepción CosmosException con el código de estado 429 en la aplicación.

Puede cambiar el número de reintentos predeterminado estableciendo el RetryOptions en la instancia de CosmosClientOptions. De forma predeterminada, la excepción CosmosException con el código de estado 429 se devuelve tras un tiempo de espera acumulativo de 30 segundos si la solicitud sigue funcionando por encima de la tasa de solicitudes. Este error se devuelve aunque el número de reintentos actual sea inferior al número máximo de reintentos, tanto si el valor actual es el predeterminado de 9 como si es un valor definido por el usuario.

El comportamiento de reintento automatizado ayuda a mejorar la resistencia y la facilidad de uso de la mayoría de las aplicaciones. Pero es posible que no sea el mejor comportamiento al realizar pruebas comparativas de rendimiento, especialmente cuando se mide la latencia. La latencia observada del cliente aumentará si el experimento llega a la limitación del servidor y hace que el SDK del cliente realice reintentos de forma silenciosa. Para evitar aumentos de latencia durante los experimentos de rendimiento, mida el gasto devuelto por cada operación y asegúrese de que las solicitudes funcionan por debajo de la tasa de solicitudes observada.

Para más información, consulte Unidades de solicitud.

Para un mayor rendimiento, diseñe para documentos más pequeños

El cargo de la solicitud (es decir, el costo de procesamiento de solicitudes) de una operación determinada se correlaciona directamente con el tamaño del documento. Las operaciones en documentos grandes cuestan más que las operaciones en documentos pequeños.

Pasos siguientes

Para obtener una aplicación de ejemplo que se usa para evaluar Azure Cosmos DB en escenarios de alto rendimiento en algunos equipos del cliente, consulte Rendimiento y pruebas de escalado con Azure Cosmos DB.

Para más información sobre cómo diseñar la aplicación para escalarla y obtener un alto rendimiento, consulte Partición y escalado en Azure Cosmos DB.