Prácticas recomendadas de consulta e indexación query-and-indexing-best-practices

AEM En as a Cloud Service, todos los aspectos operativos relacionados con la indexación están automatizados. Esto permite a los desarrolladores centrarse en crear consultas eficientes y sus definiciones de índice correspondientes.

Cuándo usar consultas when-to-use-queries

Las consultas son una forma de acceder al contenido, pero no son la única posibilidad. En muchas situaciones, se puede acceder al contenido del repositorio de forma más eficaz por otros medios. Debe tener en cuenta si las consultas son la forma mejor y más eficaz de acceder al contenido para su caso de uso.

Diseño de repositorios y taxonomías repository-and-taxonomy-design

Al diseñar la taxonomía de un repositorio, se deben tener en cuenta varios factores. Estos incluyen controles de acceso, localización, herencia de componentes y propiedades de página, etc.

Al diseñar una taxonomía que aborde estas preocupaciones, también es importante tener en cuenta la "transitabilidad" del diseño de indexación. En este contexto, la transitabilidad es la capacidad de una taxonomía para permitir que se acceda al contenido de forma predecible en función de su ruta. Esto ofrece un sistema más eficiente, fácil de mantener, que uno que requiere que se ejecuten varias consultas.

Además, al diseñar una taxonomía, es importante tener en cuenta si la ordenación es importante. En los casos en los que no se requiere un orden explícito y se espera un gran número de nodos hermanos, se prefiere utilizar un tipo de nodo sin ordenar como sling:Folder o oak:Unstructured. En los casos en que sea necesario realizar un pedido, nt:unstructured y sling:OrderedFolder sería más apropiado.

Consultas en componentes queries-in-components

AEM Dado que las consultas pueden ser una de las operaciones más gravosas realizadas en un sistema de, es aconsejable evitarlas en los componentes. Tener varias consultas ejecutándose cada vez que se procesa una página puede a menudo degradar el rendimiento del sistema. Existen dos estrategias que se pueden utilizar para evitar la ejecución de consultas al procesar componentes: atravesar nodos y recuperación previa de resultados.

Recorrido de nodos traversing-nodes

Si el repositorio está diseñado de manera que permita un conocimiento previo de la ubicación de los datos necesarios, se puede implementar un código que recupere estos datos de las rutas necesarias sin tener que ejecutar consultas para encontrarlos.

Un ejemplo de esto sería procesar contenido que se ajuste a una categoría determinada. Un método sería organizar el contenido con una propiedad category que se pueda consultar para rellenar un componente que muestre los elementos de una categoría.

Un mejor enfoque sería estructurar este contenido en una taxonomía por categoría para que se pueda recuperar manualmente.

Por ejemplo, si el contenido se almacena en una taxonomía similar a:

/content/myUnstructuredContent/parentCategory/childCategory/contentPiece

el /content/myUnstructuredContent/parentCategory/childCategory El nodo simplemente se puede recuperar, sus tareas secundarias se pueden analizar y utilizar para procesar el componente.

Además, cuando se trata de un conjunto de resultados pequeño u homogéneo, puede ser más rápido atravesar el repositorio y recopilar los nodos necesarios, en lugar de crear una consulta para devolver el mismo conjunto de resultados. Como consideración general, las consultas deben evitarse siempre que sea posible hacerlo.

Resultados de recuperación previa prefetching-results

A veces, el contenido o los requisitos relacionados con el componente no permiten el uso del recorrido de nodos como método para recuperar los datos necesarios. En estos casos, es necesario ejecutar las consultas necesarias antes de procesar el componente para garantizar un rendimiento óptimo.

Si los resultados necesarios para el componente se pueden calcular en el momento en que se crea y no hay expectativas de que el contenido cambie, la consulta se puede ejecutar después de realizar un cambio.

Si los datos o el contenido cambian con regularidad, la consulta se puede ejecutar en una programación o a través de un oyente para actualizaciones de los datos subyacentes. A continuación, los resultados se pueden escribir en una ubicación compartida del repositorio. Cualquier componente que necesite estos datos puede extraer los valores de este nodo único sin necesidad de ejecutar una consulta en tiempo de ejecución.

Se puede utilizar una estrategia similar para mantener el resultado en una caché en memoria, que se rellena al inicio y se actualiza cada vez que se realizan cambios (con un JCR) ObservationListener o un Sling ResourceChangeListener).

Optimización de consultas optimizing-queries

La documentación de Oak proporciona una información general de alto nivel sobre cómo se ejecutan las consultas. Esto forma la base de todas las actividades de optimización descritas en este documento.

AEM El as a Cloud Service proporciona el Herramienta de rendimiento de consultas, diseñado para admitir la implementación de consultas eficientes.

  • Muestra las consultas ya ejecutadas con sus características de rendimiento relevantes y el plan de consulta.
  • Permite realizar consultas ad hoc en varios niveles, desde mostrar el plan de la consulta hasta ejecutar la consulta completa.

Se puede acceder a la herramienta de rendimiento de consultas mediante el Developer Console en Cloud Manager. AEM La herramienta de rendimiento de consultas de as a Cloud Service AEM proporciona más información sobre los detalles de la ejecución de consultas en la versión 6.x de la misma en la que se ejecuta la consulta de la manera más sencilla posible.

Este gráfico ilustra el flujo general para utilizar la herramienta de rendimiento de consultas para optimizar las consultas.

Flujo de optimización de consultas

Uso de un índice use-an-index

Cada consulta debe utilizar un índice para ofrecer un rendimiento óptimo. En la mayoría de los casos, los índices existentes listos para usar deberían ser suficientes para gestionar consultas.

A veces, es necesario agregar propiedades personalizadas a un índice existente, por lo que se pueden consultar restricciones adicionales mediante el índice. Ver el documento Búsqueda de contenido e indexación para obtener más información. El Hoja de trucos de consulta JCR de este documento describe el aspecto que debe tener una definición de propiedad en un índice para admitir un tipo de consulta específico.

Uso de los criterios adecuados use-the-right-criteria

La restricción principal de cualquier consulta debe ser una coincidencia de propiedad, ya que es el tipo más eficaz. Añadir más restricciones de propiedad limita aún más el resultado.

El motor de consultas considera un solo índice. Esto significa que un índice existente puede y debe personalizarse añadiéndole más propiedades de índice personalizadas.

El Hoja de trucos de consulta JCR de este documento enumera las restricciones disponibles y también describe el aspecto que debe tener una definición de índice para que se recoja. Utilice el Herramienta de rendimiento de consultas para probar la consulta y asegurarse de que se utiliza el índice correcto y de que el motor de consultas no necesita evaluar las restricciones fuera del índice.

Pedidos ordering

Si se solicita un orden específico del resultado, el motor de consultas puede hacerlo de dos formas:

  1. El índice puede entregar el resultado por completo y en el orden correcto.

    • Esto funciona si las propiedades que se utilizan para ordenar están anotadas con ordered=true en la definición del índice.
  2. El motor de consultas realiza el proceso de ordenación.

    • Esto puede ocurrir cuando el motor de consulta realiza un filtrado fuera del índice o cuando la propiedad de ordenación no está anotada con el ordered=true propiedad.
    • Esto requiere que el conjunto de resultados completo se lea en la memoria para ordenarlo, lo que es mucho más lento que la primera opción.

Restringir el tamaño del resultado restrict-result-size

El tamaño recuperado del resultado de la consulta es un factor importante en el rendimiento de la consulta. Como el resultado se obtiene de forma diferida, existe una diferencia entre recuperar los 20 primeros resultados y recuperar 10 000 resultados, tanto en tiempo de ejecución como en uso de memoria.

Esto también significa que el tamaño del conjunto de resultados solo se puede determinar correctamente si se recuperan todos los resultados. Por este motivo, el conjunto de resultados recuperado siempre debe estar limitado, ya sea aumentando la consulta (consulte la Hoja de trucos de consulta JCR de este documento para obtener más información) o limitando las lecturas de los resultados.

Este límite también evita que el motor de consultas visite límite de recorrido de 100 000 nodos, lo que provoca una parada forzada de la consulta.

Consulte la sección Consultas con grandes conjuntos de resultados de este documento si un conjunto de resultados potencialmente grande debe procesarse completamente.

Herramienta de rendimiento de consultas query-performance-tool

La herramienta de rendimiento de consultas (ubicada en /libs/granite/operations/content/diagnosistools/queryPerformance.html y disponibles a través de Consola de desarrollador en Cloud Manager) proporciona -

  • Una lista de cualquier "Consulta lenta"; definida actualmente como aquellas que leen o analizan más de 5000 filas.
  • Una lista de 'Consultas populares'
  • La herramienta "Explicar consulta" para comprender cómo Oak ejecutará una consulta en particular.

Herramienta de rendimiento de consultas

Las tablas "Consultas lentas" y "Consultas populares" incluyen:

  • La propia instrucción de consulta.

  • Detalles del último subproceso que ejecutó la consulta, lo que permite identificar la página o función de la aplicación que ejecuta la consulta.

  • Una puntuación de "Optimización de lectura" para la consulta.

    • Se calcula como la relación entre el número de filas/nodos analizados para ejecutar la consulta y el número de resultados coincidentes leídos.
    • Una consulta para la que cada restricción (y cualquier orden) se puede administrar en el índice tendrá una puntuación del 90 % o superior.
  • Detalles del número máximo de filas -

    • Lectura: indica que se incluyó una fila como parte de un conjunto de resultados.
    • Analizado: indica que se incluyó una fila en los resultados de la consulta de índice subyacente (en el caso de una consulta indexada) o que se leyó desde el almacén de nodos (en el caso de un recorrido de repositorio).

Estas tablas ayudan a identificar consultas que no están totalmente indexadas (consulte Uso de un índice o están leyendo demasiados nodos (consulte también Recorrido del repositorio y Índice de recorrido). Estas consultas se resaltarán, con las áreas de preocupación apropiadas marcadas en rojo.

El Reset Statistics se proporciona la opción para eliminar todas las estadísticas existentes recopiladas en las tablas. Esto permite ejecutar una consulta determinada (a través de la propia aplicación o de la herramienta Explicar consulta) y analizar las estadísticas de ejecución.

Explicar la consulta

La herramienta de consulta Explicar permite a los desarrolladores comprender el plan de ejecución de la consulta (consulte Lectura del Plan de ejecución de consultas), incluidos los detalles de cualquier índice utilizado al ejecutar la consulta. Esto puede utilizarse para comprender la eficacia con la que se indexa una consulta para predecir o analizar de forma retrospectiva su rendimiento.

Explicación de una consulta

Para explicar una consulta, haga lo siguiente:

  • Seleccione el idioma de consulta adecuado mediante la variable Language lista desplegable.

  • Introduzca la sentencia de consulta en la Query field.

  • Si es necesario, seleccione cómo se ejecutará la consulta utilizando las casillas de verificación proporcionadas.

    • De forma predeterminada, no es necesario ejecutar las consultas JCR para identificar el plan de ejecución de la consulta (este no es el caso de las consultas de QueryBuilder).

    • Se proporcionan tres opciones para ejecutar la consulta:

      • Include Execution Time : ejecute la consulta pero no intente leer ningún resultado.
      • Read first page of results : ejecute la consulta y lea la primera "página" de 20 resultados (replicando las prácticas recomendadas para ejecutar consultas).
      • Include Node Count - ejecutar la consulta y leer el conjunto de resultados completo (generalmente no se recomienda esto: consulte Índice de recorrido).

Ventana emergente Explicación de la consulta query-explanation-popup

Ventana emergente Explicación de la consulta

Después de seleccionar ExplainA continuación, se muestra al usuario una ventana emergente que describe el resultado de la explicación de la consulta (y la ejecución, si está seleccionada).
Esta ventana emergente incluye detalles de lo siguiente:

  • Los índices utilizados al ejecutar la consulta (o ningún índice si la consulta se ejecutaría mediante Recorrido del repositorio).
  • El tiempo de ejecución (si Include Execution Time casilla de verificación estaba activada) y recuento de resultados leídos (si Read first page of results o Include Node Count casillas de verificación marcadas).
  • El plan de ejecución, que permite un análisis detallado de cómo se ejecuta la consulta: consulte Lectura del Plan de ejecución de consultas para saber cómo interpretar esto.
  • Las rutas de los primeros 20 resultados de la consulta (si Read first page of results casilla de verificación activada)
  • Los registros completos de la planificación de la consulta, que muestran los costes relativos de los índices que se consideraron para la ejecución de esta consulta (el índice con el coste más bajo será el elegido).

Lectura del Plan de ejecución de consultas reading-query-execution-plan

El plan de ejecución de consultas contiene todo lo necesario para predecir (o explicar) el rendimiento de una consulta determinada. Comprenda la eficacia con la que se ejecutará la consulta comparando las restricciones y el orden de la consulta JCR (o Query Builder) original con la consulta ejecutada en el índice subyacente (Lucene, Elastic o Property).

Tenga en cuenta la siguiente consulta:

/jcr:root/content/dam//element(*, dam:Asset) [jcr:content/metadata/dc:title = "My Title"] order by jcr:created

… que contiene -

  • 3 restricciones

    • Nodetype (dam:Asset)
    • Ruta (descendientes de /content/dam)
    • Propiedad (jcr:content/metadata/dc:title = "My Title")
  • Solicitud por parte de jcr:created propiedad

Explicar esta consulta resulta en el siguiente plan:

[dam:Asset] as [a] /* lucene:damAssetLucene-9(/oak:index/damAssetLucene-9) +:ancestors:/content/dam +jcr:content/metadata/dc:title:My Title ordering:[{ propertyName : jcr:created, propertyType : UNDEFINED, order : ASCENDING }] where ([a].[jcr:content/metadata/dc:title] = 'My Title') and (isdescendantnode([a], [/content/dam])) */

Dentro de este plan, la sección que describe la consulta ejecutada en el índice subyacente es -

lucene:damAssetLucene-9(/oak:index/damAssetLucene-9) +:ancestors:/content/dam +jcr:content/metadata/dc:title:My Title ordering:[{ propertyName : jcr:created, propertyType : UNDEFINED, order : ASCENDING }]

Esta sección del plan establece que:

  • Se utiliza un índice para ejecutar esta consulta:

    • En este caso, el índice Lucene /oak:index/damAssetLucene-9 se utilizarán, por lo que el resto de la información se encuentra en Sintaxis de consultas de Lucene.
  • Las 3 restricciones se gestionan mediante el índice:

    • La restricción del tipo de nodo
      • implícito, porque damAssetLucene-9 solo indexa nodos de tipo dam:Asset.
    • La restricción de ruta
      • porque +:ancestors:/content/dam aparece en la consulta de Lucene.
    • La restricción de propiedad
      • porque +jcr:content/metadata/dc:title:My Title aparece en la consulta de Lucene.
  • El índice gestiona el orden

    • porque ordering:[{ propertyName : jcr:created, propertyType : UNDEFINED, order : ASCENDING }] aparece en la consulta de Lucene.

Es probable que una consulta de este tipo funcione bien, ya que los resultados devueltos por la consulta de índice no se filtrarán más en el motor de consultas (aparte del filtrado de Control de acceso). Sin embargo, aún es posible que una consulta de este tipo se ejecute lentamente si no se siguen las prácticas recomendadas: consulte Índice de recorrido más abajo.

Consideración de una consulta diferente:

/jcr:root/content/dam//element(*, dam:Asset) [jcr:content/metadata/myProperty = "My Property Value"] order by jcr:created

… que contiene -

  • 3 restricciones

    • Nodetype (dam:Asset)
    • Ruta (descendientes de /content/dam)
    • Propiedad (jcr:content/metadata/myProperty = "My Property Value")
  • Solicitud por parte de jcr:created property**

Explicar esta consulta resulta en el siguiente plan:

[dam:Asset] as [a] /* lucene:damAssetLucene-9-custom-1(/oak:index/damAssetLucene-9-custom-1) :ancestors:/content/dam ordering:[{ propertyName : jcr:created, propertyType : UNDEFINED, order : ASCENDING }] where ([a].[jcr:content/metadata/myProperty] = 'My Property Value') and (isdescendantnode([a], [/content/dam])) */

Dentro de este plan, la sección que describe la consulta ejecutada en el índice subyacente es -

lucene:damAssetLucene-9(/oak:index/damAssetLucene-9) :ancestors:/content/dam ordering:[{ propertyName : jcr:created, propertyType : UNDEFINED, order : ASCENDING }]

Esta sección del plan establece que:

  • Solo 2 (de las 3) restricciones son manejadas por el índice -

    • La restricción del tipo de nodo
      • implícito, porque damAssetLucene-9 solo indexa nodos de tipo dam:Asset.
    • La restricción de ruta
      • porque +:ancestors:/content/dam aparece en la consulta de Lucene.
  • La restricción de propiedad jcr:content/metadata/myProperty = "My Property Value" no se ejecuta en el índice, sino que se aplica como filtro del motor de consulta a los resultados de la consulta de Lucene subyacente.

    • Esto se debe a +jcr:content/metadata/myProperty:My Property Value no aparece en la consulta de Lucene, ya que esta propiedad no está indexada en la damAssetLucene-9 índice utilizado para esta consulta.

Este plan de ejecución de consultas generará todos los recursos por debajo de /content/dam leerse desde el índice y, a continuación, filtrarse más por el motor de consultas (que solo incluirá los que coincidan con la restricción de propiedad no indizada en el conjunto de resultados).

Incluso si solo un pequeño porcentaje de recursos coincide con la restricción jcr:content/metadata/myProperty = "My Property Value", la consulta debe leer un gran número de nodos para (intentar) rellenar la "página" de resultados solicitada. Esto puede dar como resultado un rendimiento incorrecto de la consulta, que se mostrará como con un bajo Read Optimization puntuación en la herramienta de rendimiento de consultas) y puede generar mensajes ADVERTENCIA que indiquen que se está atravesando un gran número de nodos (consulte Índice de recorrido).

Para optimizar el rendimiento de esta segunda consulta, cree una versión personalizada de damAssetLucene-9 index (damAssetLucene-9-custom-1) y agregue la siguiente definición de propiedad:

"myProperty": {
  "jcr:primaryType": "nt:unstructured",
  "propertyIndex": true,
  "name": "jcr:content/metadata/myProperty"
}

Hoja de características clave de consulta JCR jcr-query-cheatsheet

Para admitir la creación de consultas JCR y definiciones de índice eficientes, la variable Hoja de características clave de consulta JCR está disponible para su descarga y uso como referencia durante el desarrollo.

Contiene consultas de ejemplo para QueryBuilder, XPath y SQL-2, que cubren varios escenarios que se comportan de forma diferente en términos de rendimiento de la consulta. También proporciona recomendaciones sobre cómo crear o personalizar índices de Oak. AEM El contenido de esta hoja de referencia se aplica a las versiones as a Cloud Service AEM y 6.5 de la.

Prácticas recomendadas de definición de índice index-definition-best-practices

A continuación se presentan algunas prácticas recomendadas a tener en cuenta al definir o ampliar índices.

  • Para tipos de nodo que tienen índices existentes (como dam:Asset o cq:Page) prefieren la extensión de los índices OOTB a la adición de nuevos índices.

    • Añadir nuevos índices, especialmente índices de texto completo, en la variable dam:Asset no se recomienda utilizar nodetype (consulte esta nota).
  • Al añadir nuevos índices

    • Defina siempre índices del tipo "lucene".
    • Utilice una etiqueta de índice en la definición del índice (y en la consulta asociada) y selectionPolicy = tag para asegurarse de que el índice solo se utiliza para las consultas deseadas.
    • Asegurar queryPaths y includedPaths se proporcionan ambos (normalmente con los mismos valores).
    • Uso excludedPaths para excluir rutas que no contendrán resultados útiles.
    • Uso analyzed propiedades solo cuando es necesario, por ejemplo, cuando necesita utilizar una restricción de consulta de texto completo únicamente para esa propiedad.
    • Especificar siempre async = [ async, nrt ] , compatVersion = 2 y evaluatePathRestrictions = true.
    • Especificar solo nodeScopeIndex = true si necesita un índice de texto completo de ámbito de nodo.
NOTE
Para obtener más información, consulte Documentación del índice de Oak Lucene.

Las comprobaciones de canalización automatizadas de Cloud Manager aplicarán algunas de las prácticas recomendadas descritas anteriormente.

Consultas con conjuntos de resultados grandes queries-with-large-result-sets

Aunque se recomienda evitar consultas con grandes conjuntos de resultados, hay casos válidos en los que deben procesarse grandes conjuntos de resultados. A menudo, el tamaño del resultado no se conoce por adelantado, por lo que se deben tomar algunas precauciones para que el procesamiento sea fiable.

  • La consulta no debe ejecutarse dentro de una solicitud. AEM En su lugar, la consulta debe ejecutarse como parte de un trabajo de Sling o de un flujo de trabajo de. No tienen limitaciones en su tiempo de ejecución total y se reinician en caso de que la instancia se desactive durante el procesamiento de la consulta y sus resultados.
  • Para superar el límite de consultas de 100 000 nodos, debe considerar la posibilidad de utilizar Paginación de conjunto de claves y divida la consulta en varias subconsultas.

Recorrido del repositorio repository-traversal

Las consultas que atraviesan el repositorio no utilizan un índice y se registran con un mensaje similar al siguiente.

28.06.2022 13:32:52.804 *WARN* [127.0.0.1 [1656415972414] POST /libs/settings/granite/operations/diagnosis/granite_queryperformance.explain.json HTTP/1.1] org.apache.jackrabbit.oak.plugins.index.Cursors$TraversingCursor Traversed 98000 nodes with filter Filter(query=select [jcr:path], [jcr:score], * from [nt:base] as a /* xpath: //* */, path=*) called by com.adobe.granite.queries.impl.explain.query.ExplainQueryServlet.getHeuristics; consider creating an index or changing the query

Con este fragmento de registro puede determinar lo siguiente:

  • La propia consulta: //*
  • El código Java que ejecutó esta consulta: com.adobe.granite.queries.impl.explain.query.ExplainQueryServlet::getHeuristics para ayudar a identificar al creador de la consulta.

Con esta información, es posible optimizar esta consulta utilizando los métodos descritos en la Optimización de consultas de este documento.

Índice de recorrido index-traversal

Las consultas que utilizan un índice, pero que aún leen grandes cantidades de nodos, se registran con un mensaje similar al siguiente (observe el término Index-Traversed en lugar de Traversed).

05.10.2023 10:56:10.498 *WARN* [127.0.0.1 [1696502982443] POST /libs/settings/granite/operations/diagnosis/granite_queryperformance.explain.json HTTP/1.1] org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndex$FulltextPathCursor Index-Traversed 60000 nodes with filter Filter(query=select [jcr:path], [jcr:score], * from [dam:Asset] as a where isdescendantnode(a, '/content/dam') order by [jcr:content/metadata/unindexedProperty] /* xpath: /jcr:root/content/dam//element(*, dam:Asset) order by jcr:content/metadata/unindexedProperty */, path=/content/dam//*)

Esto puede ocurrir por varias razones:

  1. No todas las restricciones de la consulta se pueden controlar en el índice.

    • En este caso, se está leyendo un superconjunto del conjunto de resultados final desde el índice y, a continuación, se está filtrando en el motor de consultas.
    • Esto es muchas veces más lento que aplicar restricciones en la consulta de índice subyacente.
  2. La consulta se ordena por una propiedad que no está marcada como "ordenada" en el índice.

    • En este caso, el motor de consulta debe leer todos los resultados devueltos por el índice y ordenarlos en memoria.
    • Esto es muchas veces más lento que aplicar la ordenación en la consulta de índice subyacente.
  3. El ejecutor de la consulta está intentando repetir un conjunto de resultados grande.

    • Esta situación puede ocurrir por varias razones, como se enumera a continuación:
Causa
Mitigación
La Comisión de p.guessTotal (o el uso de un guessTotal muy grande) que hace que QueryBuilder itere una gran cantidad de resultados contando resultados
Proporcionar p.guessTotal con un valor apropiado
El uso de un límite grande o ilimitado en Query Builder (es decir, p.limit=-1)
Use un valor apropiado para p.limit (idealmente 1000 o menos)
El uso de un predicado de filtrado en el Generador de consultas, que filtra grandes cantidades de resultados de la consulta JCR subyacente
Reemplace los predicados de filtrado por restricciones que se puedan aplicar en la consulta JCR subyacente
Uso de un orden basado en Comparator en QueryBuilder
Reemplazar por el orden basado en propiedades en la consulta JCR subyacente (mediante propiedades indizadas como ordenadas)
Filtrado de una gran cantidad de resultados debido al control de acceso
Aplique una propiedad indizada o una restricción de ruta adicional a la consulta para reflejar el control de acceso
El uso de "paginación de desplazamiento" con un desplazamiento grande
Considere utilizar Paginación de conjunto de claves
Iteración de números de resultados grandes o ilimitados
Considere utilizar Paginación de conjunto de claves
Índice elegido incorrecto
Utilice Etiquetas en la consulta y la definición del índice para asegurarse de que se utiliza el índice esperado
recommendation-more-help
fbcff2a9-b6fe-4574-b04a-21e75df764ab