Sugerencias de rendimiento para Azure Cosmos DB y el SDK de .NET v2

SE APLICA A: NoSQL

Azure Cosmos DB es una base de datos distribuida rápida y flexible que se escala sin problemas con una latencia y un 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 cómo aprovisionar el rendimiento del contenedor o cómo aprovisionar el rendimiento de la base de datos. Sin embargo, dado que se tiene acceso a Azure Cosmos DB a través de llamadas de red, existen optimizaciones del lado cliente que se pueden realizar para lograr el máximo rendimiento cuando se usa el del SDK de .NET para SQL.

Por lo tanto, si está intentando mejorar el rendimiento de la base de datos, tenga en cuenta estas opciones:

Actualización al SDK de .NET v3

Se publicó el SDK de .NET v3. Si usa el SDK de .NET v3, consulte la información siguiente en la guía de rendimiento de .NET v3:

  • Valores predeterminados en el modo TCP directo
  • Compatibilidad con Stream API
  • Compatibilidad con el serializador personalizado para permitir el uso de System.Text.JSON
  • Compatibilidad integrada en bloque y en masa

Hospedando recomendaciones

Activación de la recolección de elementos no utilizados (GC) 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 alto rendimiento (más de 50 000 RU/s), la aplicación del cliente podría convertirse en el cuello de botella debido a que el equipo se limita al 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 una colección 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. 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 (posteriores a la 2.16.2) lo quitan automáticamente al detectarlo. En versiones anteriores, puede quitarlo del modo siguiente:

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
}

Redes

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

El modo de conexión predeterminado del SDK de .NET V2 es la puerta de enlace. El modo de conexión se configura durante la construcción de la instancia de DocumentClient mediante el parámetro ConnectionPolicy. Si usa el modo directo, también debe establecer Protocol mediante el parámetro ConnectionPolicy. Para obtener más información sobre las distintas opciones de conectividad, vea el artículo Modos de conectividad.

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

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 conexiones de larga duración en lugar de con el protocolo HTTPS, que finaliza las conexiones tras 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:

  • Configurar la propiedad ConnectionPolicy.PortReuseMode para PrivatePortPool (eficaz con una versión de marco >= 4.6.1 y la versión de .NET Core >= 2.0): Esto permite al SDK usar un pequeño grupo de puertos efímeros para diferentes puntos de conexión de destino de Azure Cosmos DB.
  • Configure la propiedad ConnectionPolicy.IdleConnectionTimeout para que sea mayor o igual que 10 minutos. Los valores recomendados son entre 20 minutos y 24 horas.

Llamada a OpenAsync para evitar la latencia de inicio en la primera solicitud

De forma predeterminada, la primera solicitud tiene una latencia mayor porque debe capturar la tabla de enrutamiento de direcciones. Cuando use SDK V2, llame a OpenAsync() una vez durante la inicialización para evitar esta latencia de inicio en la primera solicitud. La llamada tiene el aspecto siguiente: await client.OpenAsync();

Nota:

OpenAsync generará solicitudes para obtener la tabla de enrutamiento de direcciones para todos los contenedores de la cuenta. En el caso de las cuentas que tienen muchos contenedores pero cuya aplicación tiene acceso a un subconjunto de ellos, OpenAsync generaría una cantidad innecesaria de tráfico, lo que haría que la inicialización fuera lenta. Por lo tanto, el uso de OpenAsync podría no ser útil en este escenario porque ralentiza el inicio de la aplicación.

Para el rendimiento, colocar a los clientes de 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 ms y 2 ms, pero la latencia entre la costa oeste y el 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.

La Directiva de conexión de Azure Cosmos DB

Aumentar el número de subprocesos o tareas

Dado que las llamadas a Azure Cosmos DB se realizan a través de la red, puede que tenga que cambiar el grado de paralelismo de las solicitudes para que la aplicación cliente dedique 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 la redes aceleradas

Para reducir la latencia y la vibración de la CPU, se recomienda habilitar redes aceleradas en las máquinas virtuales de cliente. Consulte Creación de una máquina virtual Windows con redes aceleradas o Creación de una máquina virtual Linux con redes aceleradas.

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 las páginas del SDK de Azure Cosmos DB para determinar las mejoras de los SDK y las revisiones más recientes.

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

Cada instancia de DocumentClient 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.

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.
  • Use ToList() en DocumentClient.CreateDocumentQuery(...) que use llamadas de bloqueo para purgar la consulta de manera sincrónica. Use AsDocumentQuery() 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.

Aumentar System.Net MaxConnections por host al usar el modo de puerta de enlace

Las solicitudes de Azure Cosmos DB se realizan a través de HTTPS o REST cuando se usa el modo de puerta de enlace. Están sujetos 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 1 000) para que la biblioteca de cliente pueda utilizar varias conexiones simultáneas para 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.

Implementación del retroceso según intervalos RetryAfter

Durante las pruebas de rendimiento, debe aumentar la carga hasta que se limite una pequeña tasa de solicitudes. Si se limitan las solicitudes, la aplicación del cliente debe volver al límite para el intervalo de reintentos especificado por el servidor. Respetar el retroceso garantiza una cantidad mínima de tiempo en espera entre reintentos.

La compatibilidad con la directiva de reintentos se incluye en estos SDK:

Para más información, consulte RetryAfter.

En la versión 1.19 y posteriores del SDK de .NET, hay un mecanismo para registrar información de diagnóstico adicional y problemas de latencia de solución de problemas, tal como se muestra en el ejemplo siguiente. Puede registrar la cadena de diagnóstico para las solicitudes que tienen una mayor latencia de lectura. La cadena de diagnóstico capturada le ayudará a comprender cuántas veces ha recibido 429 errores para una solicitud determinada.

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

Almacenamiento en caché de los identificadores URI de documentos para una latencia menor en las operaciones de lectura

Siempre que sea posible, almacene en caché los identificadores URI para obtener el mejor rendimiento en las operaciones de lectura. Debe definir la lógica para almacenar en caché el identificador de recurso al crear un recurso. Las búsquedas basadas en identificadores de recursos son más rápidas que las búsquedas basadas en nombres, por lo que el almacenamiento en caché de estos valores mejora el rendimiento.

Aumentar el número de subprocesos o tareas

Consulte Aumentar el número de subprocesos y tareas en la sección de 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 and IndexingPolicy.ExcludedPaths). Las rutas de indexación pueden mejorar el rendimiento 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, este código 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 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);

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

Throughput

Medición y optimización del uso menor de unidades de solicitud por segundo

Azure Cosmos DB ofrece un amplio conjunto de operaciones de base de datos. Estas operaciones incluyen consultas relacionales y jerárquicas con UDF, procedimientos almacenados y desencadenadores que funcionan 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 necesaria para completar la operación. En lugar de pensar y administrar los recursos de hardware, puede pensar en una unidad de solicitud (RU) como una única medida para los recursos necesarios para realizar varias operaciones de base de datos y dar servicio a una solicitud de aplicación.

El rendimiento se aprovisiona en función del número de unidades de solicitud establecidas para cada contenedor. El consumo de la unidad de solicitud se evalúa como una tarifa por segundo. 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 UDF y el tamaño del conjunto de código de origen influyen en el coste 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
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);
    }

El cargo de solicitud devuelto en este encabezado es una fracción del rendimiento aprovisionado (es decir, 2 000 RUs por segundo). 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 finalizará de forma preventiva la solicitud con RequestRateTooLarge (código de Estado HTTP 429). Devolverá 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 DocumentClientException 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 ConnectionPolicy. De forma predeterminada, la excepción DocumentClientException 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 coste 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.