Consulta SQL++ / N1QL

Entendiendo la Agrupación y Agregación de Índices en Couchbase N1QL Query

Couchbase N1QL es un moderno motor de procesamiento de consultas diseñado para proporcionar SQL agregado para JSON por índice sobre datos distribuidos con un modelo de datos flexible. Las bases de datos modernas se despliegan en clusters masivos. El uso de JSON proporciona un modo de datos flexible. N1QL soporta SQL agregado por índice para JSON para facilitar el procesamiento de consultas.

Las aplicaciones y los controladores de bases de datos envían la consulta N1QL a uno de los nodos de consulta disponibles en un clúster. El nodo de consulta analiza la consulta y utiliza los metadatos de los objetos subyacentes para determinar el plan de ejecución óptimo, que luego ejecuta. Durante la ejecución, en función de la consulta, utilizando los índices aplicables, el nodo de consulta trabaja con los nodos de índices y datos para recuperar los datos y realizar las operaciones previstas. Dado que Couchbase es una base de datos modular en clúster, puedes escalar los servicios de datos, índices y consultas para adaptarlos a tus objetivos de rendimiento y disponibilidad.

Antes de Couchbase 5.5, incluso cuando una consulta con GROUP BY y/o agregados estaba cubierta por un índice, la consulta obtenía todos los datos relevantes del indexador y realizaba la agrupación/agregación de los datos dentro del motor de consultas.

En Couchbase 5.5 se ha mejorado el planificador de consultas para que solicite de forma inteligente al indexador que realice agrupaciones y agregaciones además del escaneo de rangos. para el índice de cobertura. El indexador se ha mejorado para realizar agrupaciones, COUNT(), SUM(), MIN(), MAX(), AVG() y operaciones relacionadas sobre la marcha.  

Esto no requiere cambios en la consulta del usuario, pero sí un buen diseño del índice para cubrir la consulta y ordenar las claves del índice. No todas las consultas se beneficiarán de esta optimización, y no todos los índices pueden acelerar todas las operaciones de agrupación y agregación. Comprender los patrones adecuados te ayudará a diseñar tus índices y consultas. La agrupación y agregación de índices en el índice secundario global es compatible con ambos motores de almacenamiento: GSI estándar y GSI optimizado para memoria (MOI). La agrupación y agregación de índices sólo se admite en Enterprise Edition.

Este paso de reducción de realizar el GROUP BY y la Agregación por el indexador reduce la cantidad de transferencia de datos y E/S de disco, resultando en:

  • Mejora del tiempo de respuesta a las consultas
  • Mejor utilización de los recursos
  • Baja latencia
  • Gran escalabilidad
  • Bajo coste total de propiedad

Rendimiento

La agrupación y agregación de índices puede mejorar el rendimiento de las consultas en órdenes de magnitud y reducir drásticamente las latencias. La siguiente tabla muestra algunos ejemplos de mediciones de latencia de consulta.

Índice :

 

Consulta Descripción 5.0 Latencias 5.5 Latencias
SELECT t.type, COUNT(type) AS cnt DESDE viaje-muestra AS t WHERE t.type IS NOT NULL GROUP BY t.type;
  • GROUP BY clave de índice principal
  • Agregación
230 ms 13ms
SELECT t.type, COUNT(1) AS cnt, COUNT(DISTINCT ciudad) AS cntdciudad DESDE viaje-muestra AS t WHERE t.type IN ["hotel", "aeropuerto"]. GROUP BY t.type, t.country;
  • GROUP BY múltiples claves de índice principales
  • Agregados múltiples
  • Agregado Distinto
40ms 7ms
SELECT t.país, COUNT(ciudad) AS cnt DESDE viaje-muestra AS t DONDE t.type = "aeropuerto" GROUP BY t.country;
  • GROUP BY primera clave de índice principal sin igualdad
  • Agregación
25ms 3ms
SELECT t.ciudad, cnt DESDE viaje-muestra AS t WHERE t.type IS NOT NULL GROUP BY t.ciudad LETTING cnt = COUNT(ciudad) HAVING cnt > 0 ;
  • GROUP BY clave de índice no principal
  • cláusula de arrendamiento
  • Cláusula HAVING
300 ms 160 ms

Visión general de la agrupación y agregación de índices

 

 

La figura anterior muestra todas las posibles fases por las que pasa una consulta SELECT para devolver los resultados.  El proceso de filtrado toma el espacio de claves inicial y produce un subconjunto óptimo de los documentos que interesan a la consulta. Para producir el subconjunto más pequeño posible, se utilizan índices para aplicar tantos predicados como sea posible. El predicado de la consulta indica el subconjunto de datos que interesa. Durante la fase de planificación de la consulta, seleccionamos los índices que se van a utilizar. A continuación, para cada índice, decidimos los predicados que debe aplicar cada índice. Los predicados de consulta se traducen en escaneos de rango en el plan de consulta y se pasan al indexador.

Si la consulta no tiene JOINs y está cubierta por un índice, se pueden eliminar las fases Fetch y Join.

 

 

Cuando todos los predicados se traducen exactamente a escaneos de rango, la fase de filtrado también puede eliminarse. En esa situación, el escaneo y la agregación están uno al lado del otro, y como el indexador tiene la capacidad de hacer agregación, esa fase se puede hacer en el nodo indexador. En algunos casos, las fases de ordenación, desplazamiento y límite también pueden realizarse en el nodo del indexador.

 

 

El siguiente diagrama de flujo describe cómo el planificador de consultas decide realizar la agregación de índices para cada bloque de consulta de la consulta. Si la agregación de índices no es posible, las agregaciones se realizan en el motor de consultas.

 

 

Por ejemplo, comparemos el rendimiento anterior frente al actual del uso de GROUP BY y examinemos el plan EXPLAIN de la siguiente consulta que utiliza un índice definido en Couchbase viaje-muestra cubo:

Considere la consulta:

Antes de la versión 5.5 de Couchbase, este motor de consultas obtenía los datos relevantes del indexador y agrupaba y agregaba los datos dentro del motor de consultas. Esta sencilla consulta tarda unos 250 ms.

Ahora, en la versión 5.5 de Couchbase, esta consulta utiliza el mismo índice def_type, pero se ejecuta en menos de 20 ms. En la explicación de abajo, puedes ver menos pasos y la falta del paso de agrupación después del escaneo del índice porque el paso de escaneo del índice también hace la agrupación y agregación.

A medida que aumente la complejidad de los datos y las consultas, también lo hará el rendimiento (tanto latencia como rendimiento).   

Comprensión de EXPLAIN de la agrupación y agregación de índices

Mirando la explicación de la consulta:

Verá "index_group_aggs" en la sección IndexScan (es decir, "#operator": "IndexScan3"). Si "index_group_aggs" está ausente, el servicio de consulta está realizando la agrupación y agregación. Si la consulta está presente está utilizando la agrupación y agregación de índices y tiene toda la información relevante que el indexador requiere para la agrupación y agregación. La siguiente tabla describe cómo interpretar la diversa información del objeto index_group_aggs.

Nombre del campo Descripción Números de línea del ejemplo Explicar el texto en el ejemplo
agregados Matriz de objetos Aggregate, y cada objeto representa un agregado. La ausencia de este elemento significa que en la consulta sólo está presente group by. 14-24 agregados
  agregado Operación agregada (MAX/MIN/SUM/COUNT/COUNTN). 16 CONTAR
distinto El modificador agregado es DISTINTO Falso(Cuando es verdadero sólo aparece)
  depende Lista de posiciones clave de índice (empezando por 0) de las que depende la expresión agregada. 17-19 0 (porque el tipo es la clave 0 del índice def_type)
  expr expresión agregada 20 cubierta ((viaje-muestra.tipo))
  id ID único dado internamente y que se utilizará en proyección_índice 21 2
  keypos Indicador que indica el uso de la expresión en la posición clave del índice o del campo expr.

  • Un valor > -1 significa que la expresión agregada coincide exactamente con la posición de la clave de índice correspondiente (empezando por 0).
  • Un valor de -1 significa que la expresión agregada ] no coincide exactamente con la posición clave del índice y utiliza la expresión del campo expr.
22 0 (porque el tipo es la clave 0 del índice def_type)
depende Lista de posiciones clave de índice de las que dependen las expresiones de grupos/agregados (lista consolidada) 25-27 0
grupo Matriz de objetos GROUP BY, y cada objeto representa una clave de grupo. La ausencia de este elemento significa que no hay ninguna cláusula GROUP BY presente en la consulta. 28-37 grupo
  depende Lista de posiciones de las teclas de índice (empezando por 0) de las que depende la expresión de grupo. 30-32 0

(porque el tipo es la clave 0 del índice clave del índice def_type)

  expr expresión de grupo. 33 cubierta ((viaje-muestra.tipo))
  id ID único dado internamente y que se utilizará en proyección_índice. 34 0
  keypos Indicador que indica el uso de la expresión en la posición clave del índice o del campo expr.

  • Un valor > -1 significa que la expresión del grupo coincide exactamente con la posición de la clave de índice correspondiente (empezando por 0).
  • Un valor de -1 significa que la clave de grupo no coincide exactamente con la posición de la clave de índice y utiliza la expresión del campo expr.
35 0 (porque el tipo es la clave 0 del índice def_type)

El campo "covers" es un array que contiene todas las claves de índice, claves de documento (META().id), expresiones de claves de grupo que no coinciden exactamente con las claves de índice (ordenadas por id), agregados ordenados por id. También "Index_projection" tendrá todos los ids de grupo/agregado.

En el caso anterior, la expresión de grupo tipo es la misma clave de índice def_tipo. No se incluye dos veces.

Detalles Agrupación y agregación de índices

Utilizaremos ejemplos para mostrar cómo funciona la agrupación y agregación de índices. Para seguir los ejemplos cree un bucket "default" e inserte los siguientes documentos:

Ejemplo 1: Agrupación por claves de índice principales

Consideremos la consulta y el índice siguientes:

Índice requerido:

     La consulta tiene GROUP BY y múltiples agregados, algunos de los agregados tienen el modificador DISTINCT. La consulta se puede cubrir con el índice idx1 y el predicado (d.c0 > 0) se puede convertir en un escaneo de rango exacto y pasarlo al escaneo de índice. Por lo tanto, la combinación de índice y consulta cumple los requisitos de agrupación y agregación de índices.

Los índices se ordenan y agrupan de forma natural por el orden de definición de la clave del índice. En la consulta anterior, las claves GROUP BY (d.c0, d.c1) coinciden exactamente con las claves principales (c0, c1) del índice. Por lo tanto, el índice tiene los datos de cada grupo juntos, el indexador producirá una fila por grupo, es decir, agregación completa. Además, la consulta tiene un agregado que tiene el modificador DISTINCT y coincide exactamente con una de las claves del índice con una posición menor o igual al número de claves de grupo más una (es decir, hay 2 claves de grupo, el modificador DISTINCT puede ser cualquiera de las claves de índice en la posición 0,1,2 porque la clave de índice seguida de las claves de grupo y el modificador DISTINCT pueden aplicarse sin ordenación). Por lo tanto, la consulta anterior es adecuada para que el indexador gestione la agrupación y la agregación.

Si el grupo carece de una de las claves de índice principales y existe un predicado de igualdad, se realiza una optimización especial tratando la clave de índice implícitamente presente en las claves de grupo y determinando si la agregación completa es posible o no. Para el índice de partición, todas las claves de partición deben estar presentes en las claves de grupo para generar agregaciones completas.

 

El árbol de ejecución gráfico anterior muestra el escaneo de índices (IndexScan3) realizando agregaciones de escaneo y agrupación de índices. Se proyectan los resultados del escaneo de índices.

Veamos la explicación basada en el texto :

  • El "index_group_aggs" (líneas 24-89) en la sección IndexScan (es decir, "#operator": "IndexScan3") muestra la consulta utilizando la agrupación y agregación de índices.
  • Si la consulta utiliza agrupación y agregación de índices, los predicados se convierten exactamente en escaneos de rangos y se pasan al escaneo de índices como parte de los rangos, por lo que no habrá ningún operador Filter en la explicación.
  • Como las claves de agrupación coinciden exactamente con las claves de índice principales, el indexador producirá agregaciones completas. Por lo tanto, también eliminamos la agrupación en el servicio de consulta (no hay operadores InitialGroup, IntermediateGroup, FinalGroup en la explicación).
  • El indexador proyecta "index_projection" (líneas 99-107) incluyendo todas las claves de grupo y agregados.
  • La consulta ORDER BY coincide con las claves de índice principales y GROUP BY está en las claves de índice principales, por lo que podemos utilizar el orden de índice. Esto se puede encontrar en explicar (líneas 91-98) y no utilizará "#operator": "Order" entre las líneas 164-165.  
  • Como la consulta puede utilizar el orden del índice y no hay cláusula HAVING en la consulta, los valores "offset" y "limit" pueden pasarse al indexador.
  • Esto se puede encontrar en la línea 112, 110. El "offset" se puede aplicar sólo una vez no verá "#operator": "Offset" entre las líneas 164-165, Pero volver a aplicar el "límite" es no-op. Esto se puede ver en la línea 165-168.
  • La consulta que contiene AVG(x) se ha reescrito como SUM(x)/COUNTN(x). COUNTN(x) sólo cuenta cuando x es un valor numérico.

Ejemplo 2: Agrupación por claves de índice principales, LETTING, HAVING

Consideremos la consulta y el índice siguientes:

Índice requerido:

La consulta anterior es similar a la del ejemplo 1, pero contiene las cláusulas LETTING y HAVING. El indexador no podrá manejarlas, por lo que las cláusulas LETTING y HAVING se aplican en el servicio de consulta después de la agrupación y las agregaciones. Por lo tanto, verá los operadores Let y Filter después de IndexScan3 en el árbol de ejecución. La cláusula Having es un filtro y elimina más elementos, por lo que "offset" y "limit" no pueden enviarse al indizador y deben aplicarse en el servicio de consulta, pero aún podemos utilizar el orden del índice.

Ejemplo 3: Agrupación por claves de índice no principales

Consideremos la consulta y el índice siguientes:

Índice requerido:

    La consulta tiene GROUP BY y múltiples agregados. La consulta se puede cubrir con el índice idx1 y el predicado (d.c0 > 0) se puede convertir en escaneo de rango exacto y pasarlo a escaneo de índice. Por lo tanto, la combinación de índice y consulta cumple los requisitos de agrupación y agregación de índices.

En la consulta anterior, las claves GROUP BY (d.c1, d.c2) NO coinciden con las claves principales (c0, c1) del índice. Los grupos están dispersos por el índice. Por lo tanto, el indizador producirá varias filas por cada grupo, es decir, agregación parcial. En caso de agregación parcial, el servicio de consulta realiza la fusión de grupos, la consulta no puede utilizar el orden del índice ni enviar "offset", "limit" al indizador. En caso de agregación parcial, si algún agregado tiene el modificador DISTINCT, la agrupación de índices y la agregación no son posibles. La consulta anterior es adecuada para que el indizador gestione la agrupación y la agregación.

El árbol de ejecución gráfico anterior muestra el escaneo de índices (IndexScan3) realizando agregaciones de escaneo y agrupación de índices. Los resultados de la exploración de índices se agrupan de nuevo y se proyectan.

Veamos la explicación basada en el texto :

  • El "index_group_aggs" (líneas 24-88) en la sección IndexScan (es decir, "#operator": "IndexScan3") muestra la consulta utilizando la agrupación y agregación de índices.
  • Si la consulta utiliza agrupación y agregación de índices, los predicados se convierten exactamente en escaneos de rangos y se pasan al escaneo de índices como parte de los rangos, por lo que no habrá ningún operador Filter en la explicación.
  • Como las claves del grupo por NO coinciden con las claves principales del índice, el indexador producirá agregaciones parciales. Esto puede verse como "partial":true dentro de "index_group_aggs" en la línea 87. El servicio de consulta realiza la fusión de grupos (ver líneas 119-161)
  • Proyectos de indexador "index_projection" (líneas 91-99) que contienen claves de grupo y agregados.
  • Si el Indexador genera agregaciones parciales, la consulta no puede utilizar el orden del índice y requiere una ordenación explícita, y "offset", "limit" no pueden ser enviados al indexador. El plan tendrá operadores explícitos "Order", "Offset" y "Limit" (línea 197 - 217)
  • La consulta contiene AVG(x) que se ha reescrito como SUM(x)/COUNTN(x). COUNTN(x) sólo cuenta cuando x es un valor numérico.
  • Durante la fusión de grupos
    • MIN se convierte en MIN de MIN
    • MAX se convierte en MAX de MAX
    • SUM se convierte en SUM de SUM
    • CONTAR se convierte en SUMA de CONTAR
    • CONTN se convierte en SUMA de COUNTN
    • AVG se convierte en SUM de SUM dividido por SUM de COUNTN

Ejemplo 4: Agrupación y agregación con índice de matriz

Consideremos la consulta y el índice siguientes:

Índice requerido:

La consulta tiene GROUP BY y múltiples agregados, algunos de los agregados tienen el modificador DISTINCT. El predicado de la consulta tiene la cláusula ANY y la consulta puede cubrirse mediante el índice de matriz idxad1. El predicado (d.c0 > 0 AND d,c11 >= 10 AND ANY v IN d.a1 SATISFIES v.id = 3 END ) pueden convertirse en escaneos de rango exacto y pasarse al escaneo de índice. En el caso de los índices de matrices, el indexador mantiene un elemento independiente para cada clave de índice de matriz. Para utilizar la agrupación y agregación de índices, el predicado SATISFIES debe tener un único predicado de igualdad y la clave de índice de matriz debe tener el modificador DISTINCT. Por lo tanto, la combinación de índice y consulta es adecuada para gestionar la agrupación y agregación de índices.

Este ejemplo es similar al ejemplo 1 excepto que utiliza un índice de matriz. El árbol gráfico de ejecución anterior muestra el escaneo de índice (IndexScan3) realizando escaneo, agregaciones de agrupación de índice, orden, desplazamiento y límite. Se proyectan los resultados del escaneo de índice.

Ejemplo 5: Agrupación y agregación de la operación UNNEST

Consideremos la consulta y el índice siguientes:

Índice requerido:

La consulta tiene GROUP BY y múltiples agregados. La consulta tiene UNNEST en el array d.a1 y un predicado en la clave del array (v.id > 0). El índice idxaa1 califica la consulta (para que Unnest utilice el índice de la matriz para el escaneo del índice, el índice de la matriz debe ser la clave principal y la variable de la matriz en la definición del índice debe coincidir con el alias UNNEST). El predicado (v.id > 0) puede convertirse en escaneo de rango exacto y pasarse al escaneo de índice. Por lo tanto, la combinación de índice y consulta es adecuada para gestionar agrupaciones y agregaciones de índices.

El árbol de ejecución gráfico anterior muestra el escaneo de índices (IndexScan3) realizando escaneos, agregaciones de agrupación de índices. Los resultados del escaneo de índices se proyectan. El UNNEST es un tipo especial de JOIN entre el padre y cada elemento del array. Por lo tanto, el UNNEST repite los campos del documento padre (d.c0, d.c1) y el d.c0, dc.1 referencia tendría duplicados en comparación con el original d (Es necesario tenerlo en cuenta al utilizar SUM(), AVG()).

Reglas de agrupación y agregación de índices

  La agrupación y agregación de índices se realiza por bloque de consulta, y la decisión de utilizar o no la agrupación/agregación de índices sólo se toma tras el proceso de selección de índices.

  • El bloque de consulta no debe contener Joins, NEST, SUBconsultas.
  • El bloque de consulta debe estar cubierto por un índice unifilar.
  • El bloque de consulta no debe contener ARRAY_AGG()
  • El bloque de consulta no puede correlacionarse
  • Todos los predicados deben traducirse exactamente en exploraciones de rangos.
  • Las expresiones GROUP BY y Aggregate no pueden hacer referencia a subconsultas, parámetros con nombre ni parámetros posicionales.
  • Claves GROUP BY, las expresiones agregadas pueden ser claves de índice, claves de documento, expresiones sobre claves de índice o expresiones sobre claves de documento.
  • El índice debe ser capaz de agrupar y agregar todos los agregados en el bloque de consulta, de lo contrario no habrá agregación del índice. (es decir, ALL o None)
  • El agregado contiene el modificador DISTINCT
    • Las claves de grupo deben coincidir exactamente con las claves de índice principales (si la consulta contiene un predicado de igualdad en la clave de índice, asume que esta clave de índice se incluye implícitamente en las claves de GRUPO si no está ya presente).
    • La expresión agregada debe estar en una de las claves de índice principales n+1 (n representa el número de claves de grupo).
    • En caso de índice de partición, las claves de partición deben coincidir exactamente con las claves de grupo.

Resumen

Cuando analice el plan de explicación, correlacione los predicados en la explicación con los rangos y asegúrese de que todos los predicados se traducen exactamente a escaneos de rangos y que la consulta está cubierta. Asegúrese de que la consulta utiliza agrupaciones y agregaciones de índices y, si es posible, utilice agregaciones completas del indexador ajustando las claves de índice para mejorar el rendimiento.

Comparte este artículo
Recibe actualizaciones del blog de Couchbase en tu bandeja de entrada
Este campo es obligatorio.

Author

Posted by Sitaram Vemulapalli

Sitaram Vemulapalli is a Principal Software Engineer at Couchbase. Prior to Couchbase, he served as an architect for IBM Informix SQL and has more than 20 years experience in database design and development. Sitaram holds a master's degree in system science and automation from the Indian Institute of Science, India.

Deja un comentario

¿Listo para empezar con Couchbase Capella?

Empezar a construir

Consulte nuestro portal para desarrolladores para explorar NoSQL, buscar recursos y empezar con tutoriales.

Utilizar Capella gratis

Ponte manos a la obra con Couchbase en unos pocos clics. Capella DBaaS es la forma más fácil y rápida de empezar.

Póngase en contacto

¿Quieres saber más sobre las ofertas de Couchbase? Permítanos ayudarle.