Optimización del rendimiento de consultas con Azure Cosmos DB

SE APLICA A: NoSQL

Azure Cosmos DB proporciona una API para NoSQL para consultar datos, sin necesidad de índices de esquema o secundarios. En este artículo se proporciona la siguiente información para los desarrolladores:

  • Detalles de alto nivel sobre cómo funciona la ejecución de consultas SQL de Azure Cosmos DB
  • Sugerencias y procedimientos recomendados para el rendimiento de consultas
  • Ejemplos de cómo usar métricas de ejecución de consultas SQL para depurar el rendimiento de las consultas

Acerca de la ejecución de consultas SQL

En Azure Cosmos DB, los datos se almacenan en contenedores, que pueden crecer hasta cualquier tamaño de almacenamiento o rendimiento de las solicitudes. Azure Cosmos DB escala los datos de forma fluida entre particiones físicas en segundo plano para controlar el crecimiento de los datos o los aumentos del rendimiento aprovisionado. Puede emitir consultas SQL a cualquier contenedor mediante la API de REST o uno de los SDK de SQL compatibles.

Una breve introducción a la creación de particiones: se define una clave de partición como "ciudad", que determina la forma en que los datos se dividen entre particiones físicas. Los datos que pertenecen a una única clave de partición (por ejemplo, "city" == "Seattle") se almacenan en una partición física y una única partición física puede almacenar datos de varias claves de partición. Cuando una partición física alcanza su límite de almacenamiento, el servicio la divide sin ningún problema en dos nuevas particiones. Los datos se distribuyen de manera uniforme entre las nuevas particiones, y todos los datos de una sola clave de partición se mantienen agrupados. Como las particiones son transitorias, las API usan una abstracción de un intervalo de claves de partición, que indica los intervalos de valores hash de clave de partición.

Cuando se emite una consulta a Azure Cosmos DB, el SDK realiza estos pasos lógicos:

  • Analiza la consulta SQL para determinar el plan de ejecución de consultas.
  • Si la consulta incluye un filtro con la clave de partición, como SELECT * FROM c WHERE c.city = "Seattle", se enruta a una única partición. Si la consulta no tiene un filtro en la clave de partición, se ejecuta en todas las particiones y los resultados de cada una se combinan en el lado cliente.
  • La consulta se ejecuta dentro de cada partición en serie o en paralelo, en función de la configuración del cliente. En cada partición, la consulta podría realizar uno o varios viajes en función de la complejidad de la consulta, el tamaño de página configurado y el rendimiento aprovisionado de la colección. Cada ejecución devuelve el número de unidades de solicitud que se consumen en la ejecución de la consulta y estadísticas de ejecución de consultas.
  • El SDK realiza un resumen de los resultados de la consulta entre particiones. Por ejemplo, si la consulta implica una cláusula ORDER BY entre particiones, los resultados de particiones individuales se ordenan mediante combinación para devolver resultados en orden clasificado globalmente. Si la consulta es una agregación como COUNT, los recuentos de particiones individuales se suman para generar el recuento total.

Los SDK ofrecen diversas opciones para la ejecución de consultas. Por ejemplo, en .NET estas opciones están disponibles en la clase QueryRequestOptions. En la tabla siguiente se describen estas opciones y cómo pueden afectar al tiempo de ejecución de la consulta.

Opción Descripción
EnableScanInQuery Solo es aplicable si la indexación para la ruta de filtro solicitada está deshabilitada. Se debe establecer en true si ha optado por no indexar y quiere ejecutar consultas mediante un examen completo.
MaxItemCount El número máximo de elementos que se devolverán por recorrido de ida y vuelta al servidor. Lo puede establecer en -1 para dejar que el servidor administre el número de elementos que se van a devolver.
MaxBufferedItemCount El número máximo de elementos que se pueden almacenar en búfer en el lado cliente durante la ejecución de consultas en paralelo. Un valor de propiedad positivo limita el número elementos en búfer al valor establecido. Lo puede establecer en un valor menor que 0 para dejar que el sistema decida automáticamente el número de elementos que se almacenarán en búfer.
MaxConcurrency Obtiene o establece el número de operaciones simultáneas que se ejecutan en el lado cliente durante la ejecución de consultas en paralelo. Un valor de propiedad positivo limita el número de operaciones simultáneas al valor establecido. Lo puede establecer en un valor menor que 0 para dejar que el sistema decida automáticamente el número de operaciones simultáneas que se ejecutarán.
PopulateIndexMetrics Habilita la recopilación de métricas de índice para comprender cómo el motor de consultas ha usado los índices existentes y cómo podría usar posibles índices nuevos. Esta opción incurre en sobrecarga, por lo que solo se debe habilitar al depurar consultas lentas.
ResponseContinuationTokenLimitInKb Puede limitar el tamaño máximo del token de continuación devuelto por el servidor. Es posible que tenga que establecer esto si el host de la aplicación tiene límites para el tamaño del encabezado de respuesta, pero puede aumentar la duración general y las RU consumidas para la consulta.

Por ejemplo, esta es una consulta en un contenedor con particiones creadas por /city mediante el SDK de .NET:

QueryDefinition query = new QueryDefinition("SELECT * FROM c WHERE c.city = 'Seattle'");
QueryRequestOptions options = new QueryRequestOptions()
{
    MaxItemCount = -1,
    MaxBufferedItemCount = -1,
    MaxConcurrency = -1,
    PopulateIndexMetrics = true
};
FeedIterator<dynamic> feedIterator = container.GetItemQueryIterator<dynamic>(query);

FeedResponse<dynamic> feedResponse = await feedIterator.ReadNextAsync();

Cada de ejecución de una consulta se corresponde a una API de REST POST con encabezados establecidos para las opciones de solicitud de consulta y la consulta SQL en el cuerpo. Para más información sobre los encabezados de solicitud y las opciones de la API de REST, consulte Querying resources using the REST API (Consulta de recursos mediante la API de REST).

Procedimientos recomendados para el rendimiento de consultas

Los siguientes factores suelen tener el mayor efecto en el rendimiento de las consultas de Azure Cosmos DB. Se profundizará más en cada uno de estos factores en este artículo.

Factor Sugerencia
Rendimiento aprovisionado Mida la RU por consulta y asegúrese de que tiene el rendimiento aprovisionado necesario para sus consultas.
Creación de particiones y claves de partición Dé prioridad a las consultas con el valor de clave de partición de baja latencia en la cláusula de filtro.
SDK y opciones de consulta Siga los procedimientos recomendados del SDK, como las opciones de conectividad directa y optimización de la ejecución de consultas de cliente.
Latencia de red Ejecute la aplicación en la misma región de Azure que la cuenta de Azure Cosmos DB, siempre que sea posible, para reducir la latencia.
Directiva de indexación Asegúrese de que dispone de las rutas y directivas de indexación necesarias para la consulta.
Métricas de ejecución de consultas Analice las métricas de ejecución de consultas para identificar posibles reescrituras de formas de datos y consultas.

Rendimiento aprovisionado

En Azure Cosmos DB, se crean contenedores de datos, cada uno con un rendimiento reservado expresado en unidades de solicitud (RU) por segundo. Una lectura de un documento de 1 KB es 1 RU, y cada operación (incluidas las consultas) se normaliza a un número fijo de RU según su complejidad. Por ejemplo, si tiene 1000 RU/s aprovisionadas para el contenedor y tiene una consulta como SELECT * FROM c WHERE c.city = 'Seattle' que consume 5 RU, puede ejecutar (1000 RU/s)/(5 RU/consulta) = 200 de estas consultas por segundo.

Si envía más de 200 consultas por segundo (u otras operaciones que saturan todas las RU aprovisionadas), el servicio empieza a limitar la velocidad de las solicitudes entrantes. Los SDK controlan automáticamente esta limitación de velocidad mediante la realización de un retroceso/reintento; por tanto, es posible que advierta una latencia más alta para estas consultas. El aumento del rendimiento aprovisionado al valor necesario mejora la latencia de las consultas y el rendimiento.

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

Creación de particiones y claves de partición

Con Azure Cosmos DB, los siguientes escenarios para leer datos se ordenan de los normalmente más rápidos o eficaces a los más lentos o menos eficaces.

  • GET en una sola clave de partición e identificador de elemento, también conocido como lectura de punto
  • Consulta con una cláusula de filtro en una única clave de partición
  • Consulta con una cláusula de filtro de igualdad o intervalo en cualquier propiedad
  • Sin filtros de consulta

Las consultas que necesitan ejecutarse en todas las particiones tienen una mayor latencia y pueden consumir un mayor número de RU. Puesto que cada partición tiene indexación automática en todas las propiedades, la consulta se puede atender eficazmente desde el índice en este caso. Puede acelerar las consultas que abarcan particiones mediante las opciones de paralelismo.

Para aprender más sobre la creación de particiones y las claves de particiones, consulte Creación de particiones en Azure Cosmos DB.

SDK y opciones de consulta

Vea Sugerencias de rendimiento de consultas y Pruebas de rendimiento para saber cómo obtener el mejor rendimiento del lado cliente de Azure Cosmos DB al usar nuestros SDK.

Latencia de red

Vea Distribución global de Azure Cosmos DB para saber cómo configurar la distribución global y conectarse a la región más cercana. La latencia de red tiene un efecto significativo en el rendimiento de consultas cuando es necesario realizar varios recorridos de ida y vuelta, o recuperar un gran conjunto de resultados de la consulta.

Puede usar métricas de ejecución de consultas para recuperar el tiempo de ejecución del servidor de consultas, lo que le permite diferenciar entre el tiempo invertido en la ejecución de la consulta y el tiempo empleado en el tránsito de la red.

Directiva de indexación

Vea Configuración de la directiva de indexación para obtener rutas de indexación, clases y modos, y cómo afectan a la ejecución de consultas. De manera predeterminada, Azure Cosmos DB aplica indexación automática a todos los datos y usa índices de intervalo para cadenas y números, lo que es eficaz para las consultas de igualdad. Para escenarios de inserción de alto rendimiento, considere la posibilidad de excluir rutas de acceso a fin de reducir el costo de RU para cada operación de inserción.

Puede usar las métricas de índice a fin de identificar qué índices se usan para cada consulta y si faltan índices que mejorarían el rendimiento de las consultas.

Métricas de ejecución de consultas

Se devuelven métricas detalladas para cada ejecución de consulta en el diagnóstico para la solicitud. Estas métricas describen dónde se invierte el tiempo durante la ejecución de las consultas y habilitan la solución de problemas avanzada.

Más información sobre cómo obtener las métricas de consulta.

Métrica Unidad Descripción
TotalTime milisegundos Tiempo total de ejecución de consulta
DocumentLoadTime milisegundos Tiempo invertido en la carga de documentos
DocumentWriteTime milisegundos Tiempo dedicado a escribir y serializar los documentos de salida
IndexLookupTime milisegundos Tiempo invertido en la capa física de índice
QueryPreparationTime milisegundos Tiempo invertido en preparar la consulta
RuntimeExecutionTime milisegundos Tiempo total de ejecución de runtime de consulta
VMExecutionTime milisegundos Tiempo invertido en el tiempo de ejecución de consultas que ejecuta la consulta
OutputDocumentCount count Número de documentos de salida en el conjunto de resultados
OutputDocumentSize count Tamaño total de los documentos generados, en bytes
RetrievedDocumentCount count Número total de documentos recuperados
RetrievedDocumentSize bytes Tamaño total de los documentos recuperados, en bytes
IndexHitRatio relación [0,1] Relación entre el número de documentos coincidentes mediante el filtro y el número de documentos cargados

Los SDK de cliente pueden realizar internamente varias solicitudes de consulta para atender la consulta dentro de cada partición. El cliente realiza más de una llamada por partición si los resultados totales superan la opción de solicitud de número máximo de elementos, si la consulta supera el rendimiento aprovisionado de la partición, si la carga de consultas alcanza el tamaño máximo por página o si la consulta alcanza el límite de tiempo de espera asignado al sistema. Cada ejecución de consultas parcial devuelve métricas de consulta para esa página.

Estos son algunos ejemplos de consultas y cómo interpretar algunas de las métricas devueltas en la ejecución de consultas:

Consultar Métrica de ejemplo Descripción
SELECT TOP 100 * FROM c "RetrievedDocumentCount": 101 El número de documentos recuperados es 100 + 1 para coincidir con la cláusula TOP. El tiempo de la consulta se invierte principalmente en WriteOutputTime y DocumentLoadTime dado que es un examen.
SELECT TOP 500 * FROM c "RetrievedDocumentCount": 501 RetrievedDocumentCount es ahora mayor (500 + 1 para coincidir con la cláusula TOP).
SELECT * FROM c WHERE c.N = 55 "IndexLookupTime": "00:00:00.0009500" 0,9 ms se invierten en IndexLookupTime en una búsqueda de claves, porque es una búsqueda de índice en /N/?.
SELECT * FROM c WHERE c.N > 55 "IndexLookupTime": "00:00:00.0017700" Un poco más de tiempo (1,7 ms) se invierte en IndexLookupTime durante un examen de intervalo, porque es una búsqueda de índice en /N/?.
SELECT TOP 500 c.N FROM c "IndexLookupTime": "00:00:00.0017700" El mismo tiempo se invierte en DocumentLoadTime que en las consultas anteriores, pero menos DocumentWriteTime porque solo se proyecta una propiedad.
SELECT TOP 500 udf.toPercent(c.N) FROM c "RuntimeExecutionTime": "00:00:00.2136500" Unos 213 ms se invierten en RuntimeExecutionTime al ejecutar el UDF en cada valor de c.N.
SELECT TOP 500 c.Name FROM c WHERE STARTSWITH(c.Name, 'Den') "IndexLookupTime": "00:00:00.0006400", "RuntimeExecutionTime": "00:00:00.0074100" Aproximadamente 0,6 ms se invierten en IndexLookupTime en /Name/?. La mayoría del tiempo de ejecución de consulta (~ 7 ms) se invierte en RuntimeExecutionTime.
SELECT TOP 500 c.Name FROM c WHERE STARTSWITH(LOWER(c.Name), 'den') "IndexLookupTime": "00:00:00", "RetrievedDocumentCount": 2491, "OutputDocumentCount": 500 La consulta se realiza como un examen porque se emplea LOWER, y se devuelven 500 de los 2491 documentos recuperados.

Pasos siguientes