Ajustes avanzados: encontrar y resolver búsquedas lentas en Elasticsearch

Elasticsearch es una aplicación muy flexible y con múltiples funciones que proporciona muchas maneras diferentes para buscar en tus datos. ¿Alguna vez experimentaste durante la búsqueda una velocidad más lenta de lo que esperabas? Con un sistema distribuido como Elasticsearch, puede haber varios factores posibles que contribuyan al rendimiento de la búsqueda, incluidos los factores externos como la configuración de los equilibradores de carga, la latencia de la red (ancho de banda, controladores/tarjetas NIC), etc.

En este blog, analizaremos qué puede causar búsquedas lentas y cómo identificarlas en el contexto de Elasticsearch. En este artículo partimos de algunos de nuestros métodos de solución de problemas comunes que pueden requerir cierta familiaridad con el funcionamiento de Elasticsearch.

Causas comunes de búsquedas lentas en Elasticsearch

Antes de pasar a los casos más complejos, comencemos por algunas de las causas más comunes de las consultas lentas y sus soluciones.

Síntoma: Uso alto de recursos durante inactividad

Cada shard consume recursos (CPU/memoria). Incluso cuando no hay ninguna solicitud de búsqueda/indexación, la presencia de shards consume sobrecargas del cluster.

Problema

El cluster tiene demasiados shards, al punto que la ejecución de cualquier búsqueda puede parecer lenta. Una buena regla es asegurarse de mantener menos de 20 shards no congelados por nodo por GB de heap configurado.

Solución

Reduce el recuento de shards, congela los índices o agrega nodos adicionales para manejar la carga. Considera una arquitectura caliente-tibia (funciona bien para los índices basados en el tiempo) junto con la característica de rollover/reducción en Elasticsearch para administrar de manera eficiente el recuento de shards. En cualquier despliegue, es una buena idea comenzar por realizar una planificación de capacidad adecuada con el fin de determinar la cantidad óptima de shards para cada caso de uso de búsqueda.

Síntoma: Recuento alto de rechazos en el thread pool

El thread pool de búsqueda muestra un recuento “rejected” (rechazos) cada vez más alto acumulativo basado en el último reinicio del cluster.

GET /_cat/thread_pool/search?v&h=node_name,name,active,rejected,completed

La respuesta tiene el siguiente aspecto:

node_name             name   active rejected completed
instance-0000000001   search      0       10         0
instance-0000000002   search      0       20         0
instance-0000000003   search      0       30         0

Problema

Las búsquedas están destinadas a demasiados shards y superan la cantidad de cores en el cluster. Esto crea tareas en cola en el thread pool de búsqueda y lleva a los rechazos de búsquedas. Otra causa común es la E/S del disco lenta que provoca la acumulación de tareas en la cola o, en ocasiones, una saturación total de la CPU. 

Solución

Adopta un modelo 1 dato primario : 1 réplica cuando crees índices. La plantilla de índice es una buena manera de lanzar esta configuración al momento del índice. (1P:1R será la opción predeterminada en Elasticsearch 7.0 o posteriores). Elasticsearch 5.1 o posterior soporta la cancelación de la tarea de búsqueda,que puede resultar útil cuando la búsqueda lenta aparece en la API de administración de tareas. Para mejorar la E/S del disco, ve nuestras recomendaciones de almacenamiento y asegúrate de usar el hardware recomendado para lograr el rendimiento máximo.

Síntoma: Latencia alta de indexación y CPU

La correlación de métricas muestra una latencia alta de indexación y uso de CPU cuando el cluster está saturado.

Problema

El cluster realiza muchas tareas de indexación, lo que afecta el rendimiento de búsquedas.

Solución

Aumentar index.refresh_ interval (cantidad de tiempo entre el momento de indexación de un documento y el momento en que se vuelve visible) a un valor como 30 s generalmente ayuda a mejorar el rendimiento de indexación. Las cifras reales pueden variar, por lo que las pruebas son fundamentales. Esto garantiza que los shards no tengan que trabajar demasiado y crear un segmento nuevo cada 1 segundo (valor predeterminado).

Para un caso de uso con muchas tareas de indexación, echa un vistazo a nuestras recomendaciones de ajustes de índices para optimizar tanto el rendimiento del índice como de la búsqueda.

Síntoma: Mayor latencia con más shards de réplica

Se puede observar latencia de la búsqueda después de aumentar el recuento de shards de réplica (por ejemplo, de 1 a 2). Si hay más datos presentes, los datos almacenados en caché se desalojarán antes, lo que aumenta las fallas de la página del sistema operativo.

Problema

La caché del sistema de archivos no tiene memoria suficiente para almacenar en caché las partes buscadas con frecuencia del índice. La caché de búsqueda de Elasticsearch implementa una política de desalojo de LRU: cuando se llena la caché, se desaloja el dato usado menos recientemente para dejar lugar a los datos nuevos.

Solución

Separa al menos el 50 % de la RAM física para la caché del sistema de archivos. Cuanta más memoria haya, más se podrá almacenar en caché, especialmente si el cluster está experimentando problemas de E/S. Suponiendo que el tamaño de heap se configuró correctamente, cualquier RAM física restante que pueda usarse para la caché del sistema de archivos contribuye para acelerar el rendimiento de la búsqueda.

Por ejemplo, un servidor de 128 GB de RAM tiene 30 GB separados para heap y la memoria restante para la caché del sistema de archivos (en ocasiones llamada caché del SO), que es una manera en la que el sistema operativo almacena en caché los bloques de datos de 4 KB a los que se accedió recientemente; de modo que si lees los mismos archivos una y otra vez, no necesitarás recurrir al disco la mayor parte del tiempo, la solicitud de lectura se responderá directamente desde la memoria.

Además de la caché del sistema de archivos, Elasticsearch usa caché de búsqueda y caché de solicitud para acelerar las búsquedas. Es posible optimizar todas estas memorias caché usando search-request-preference para enrutar ciertas solicitudes de búsqueda al mismo conjunto de shards siempre en lugar de alternar entre las distintas copias disponibles. De este modo, se hará un mejor uso de la caché de solicitud, la caché de búsqueda de nodo y la caché del sistema de archivos.

Síntoma: Uso alto al compartir recursos

El sistema operativo muestra un uso alto constante de la CPU y E/S del disco. Se puede ver la ganancia de rendimiento después de detener las aplicaciones de terceros.

Problema

Hay contención de recursos (CPU o E/S del disco) entre otros procesos (por ejemplo, Logstash) y Elasticsearch en sí.

Solución

Evita ejecutar Elasticsearch junto con otras aplicaciones que demanden gran cantidad de recursos en hardware compartido.

Síntoma: Uso alto de heap al agregar campos sumamente exclusivos

Mal rendimiento al buscar en campos agregados que contienen valores sumamente exclusivos (por ejemplo, ID, nombres de usuario, direcciones de correo electrónico, etc.). Durante el análisis de volcado de heap, deberías ver objetos Java con términos como “search”, “buckets”, “aggregation”, etc. que consumen una gran parte de la memoria heap.

Problema

Las agregaciones se ejecutan en campos de alta cardinalidad que requieren muchos recursos para obtener muchas cubetas. También puede haber agregaciones anidadas que involucren campos anidados o campos de combinación.

Solución

Para mejorar el rendimiento de las agregaciones de términos de alta cardinalidad, lee este blog de un colega del equipo Consultoría: https://www.elastic.co/es/blog/improving-the-performance-of-high-cardinality-terms-aggregations-in-elasticsearch

Si deseas realizar más ajustes, echa un vistazo a nuestras recomendaciones sobre campos anidados y campos de combinación para mejorar aún más tu rendimiento de agregación.

Búsquedas lentas ocasionales

En términos generales, las búsquedas lentas ocasionales o intermitentes pueden beneficiarse de algunas de las recomendaciones de ajustes de índice o ajustes de búsqueda. Las búsquedas lentas ocasionales deben estar estrechamente correlacionadas con una o más de estas métricas de monitoreo:

Elasticsearch tiene otra característica útil denominada adaptive replica selection (selección de réplica adaptable, ARS) que permite que el nodo de coordinación esté al tanto de la carga en los nodos de datos y le permite elegir las mejores copias de shards para ejecutar una búsqueda, y mejora así el rendimiento de búsqueda y la latencia. La ARS puede ser de gran ayuda para demoras ocasionales dado que distribuye de manera más uniforme la carga al momento de la búsqueda. En Elasticsearch 7.0 y versiones posteriores, la ARS estará activada de forma predeterminada.

Búsquedas lentas constantes

En el caso de las búsquedas lentas constantes, podemos intentar quitar características de la búsqueda una por una y comprobar si la búsqueda aún es lenta. Hallar la búsqueda más simple que reproduzca el problema de rendimiento ayuda a aislar e identificar el problema:

  • ¿Sigue lenta sin resaltado?
  • ¿Sigue lenta sin agregaciones?
  • ¿Sigue lenta si size está configurado en 0? (Cuandosize está configurado en 0, Elasticsearch almacena en caché los resultados de las solicitudes de búsqueda para acelerar la búsqueda).

¿Puede beneficiarse de algunas de las recomendaciones de “ajustes de búsqueda”?

Durante la solución de problemas, suele ser útil para lo siguiente:

  • Obtener una respuesta a la búsqueda con el perfil activado.
  • Recopilar la salida de hilos calientes de nodos con la búsqueda ejecutándose en un bucle while(true). Esto ayudará a comprender dónde se invierte el tiempo de CPU.
  • Perfil para la búsqueda que usa esta versión más fácil de usar de la API de perfil.

Si una búsqueda proviene de una visualización de Kibana, usa el visualization spy panel (panel espía de visualizaciones) (Kibana versión 6.3 y anteriores) o el dashboard inspect panel (panel de inspección de dashboards) (Kibana versión 6.4 y posteriores) para ver y exportar la solicitud de búsqueda real e importarla a la API de perfil para un análisis más profundo.

Descubrimiento de búsquedas lentas o costosas

En ocasiones, puede resultar difícil descubrir búsquedas lentas o costosas debido a que se procesan diferentes solicitudes/hilos de forma concurrente en una aplicación distribuida como Elasticsearch. Es aún más complicado cuando no hay control sobre la ejecución, por parte del usuario, de búsquedas costosas que disminuyen el rendimiento del cluster (por ejemplo, ciclos de recolección de basura [GC] prolongados) o, incluso peor, una situación en la que no quede memoria (OOM).

En Elasticsearch versión 7.0, presentamos una estrategia nueva de interrupción de circuito que mide el uso real de la memoria heap cuando se está reservando la memoria. Esta estrategia nueva mejora la resistencia de nodos frente a búsquedas costosas que causan una sobrecarga del cluster, está activada de forma predeterminada y puede controlarse con la configuración nueva del cluster indices.breaker.total.use_real_memory.

Sin embargo, debemos tener en cuenta que estos son mejores esfuerzos; en las situaciones que no están contempladas en ellos, sería útil recopilar el volcado de heap luego de una falla por OOM o el volcado de heap de una JVM en ejecución para comprender mejor la causa raíz.

Elasticsearch tiene otra configuración de protección (límite de software de cubetas máximo) para proteger el cluster contra OOM. Esta configuración de máximo de agregación de cubetas detiene la ejecución y hace fallar la solicitud de búsqueda cuando la cantidad de cubetas (el valor predeterminado en la versión 7.0 es de 10 000) se supera (por ejemplo, cuando se ejecutan múltiples capas de agregaciones).

Para identificar más búsquedas posiblemente costosas, podemos establecer una configuración de interrupción del circuito (indices.breaker.request.limit) comenzando con un umbral bajo para aislar las búsquedas y aumentándolo gradualmente para acotar los resultados hasta ciertas búsquedas específicas.

Logs de demora

Las búsquedas lentas también pueden identificarse activando los logs de demora en Elasticsearch. Los logs de demora funcionan específicamente a nivel de los shards, lo que significa que solo aplican los nodos de datos. Los nodos del cliente/solo coordinación están excluidos debido a que no retienen datos (índices/shards).

Los logs de demora ayudan a responder preguntas como las siguientes:

  • ¿Cuánto demoró una búsqueda?
  • ¿Cuál era el contenido del cuerpo de la solicitud de búsqueda?

Ejemplo de salida de un log de demora:

[2019-02-11T16:47:39,882][TRACE][index.search.slowlog.query] [2g1yKIZ] [logstash-20190211][4] took[10.4s], took_millis[10459], total_hits[16160], types[], stats[], 
search_type[QUERY_THEN_FETCH], total_shards[10], source[{"size":0,"query":{"bool":{"must":[{"range":{"timestamp":{"from":1549266459837,"to":1549871259837,"include_lower":true,
"include_upper":true,"format":"epoch_millis","boost":1.0}}}],"adjust_pure_negative":true,"boost":1.0}},"_source":{"includes":[],"excludes":[]},"stored_fields":"*","docvalue_fields":
[{"field":"timestamp","format":"date_time"},{"field":"utc_time","format":"date_time"}],"script_fields":{"hour_of_day":{"script":{"source":"doc['timestamp'].value.getHourOfDay()",
"lang":"painless"},"ignore_failure":false}},"aggregations":{"maxAgg":{"max":{"field":"bytes"}},"minAgg":{"min":{"field":"bytes"}}}}], id[]],

Desglose del mensaje del log de demora:

ElementoDescripción
[2019-02-11T16:47:39,882]Fecha de la búsqueda
[TRACE]Nivel de log
[index.search.slowlog.query]Fase de búsqueda del log de demora de búsqueda
[2g1yKIZ]Nombre del nodo
[logstash-20190211]Nombre del índice
[4]Cantidad de shards en la búsqueda ejecutada
took[10.4s]Tiempo de procesamiento dedicado al shard [4]. Nota: Cuando miramos logs de demora, debemos evitar sumar todos los tiempos de los distintos shards, dado que cada shard puede estar ejecutándose en paralelo.
took_millis[10459]Tiempo en milisegundos
total_hits[16160]Total de resultados
search_type[QUERY_THEN_FETCH]Tipo de búsqueda
total_shards[10]Total de shards del índice
source[]Cuerpo de la búsqueda que se ejecutó

Logs de auditoría

Los clientes con unasuscripción Oro o Platino, que incluye características de seguridad de Elastic, pueden activar los logs de auditoría para capturar más detalles sobre la búsqueda. (Los usuarios pueden iniciar una prueba por 30 días para probar las características de seguridad de Elastic). Los logs de auditoría ayudan a responder preguntas como las siguientes:

  • ¿Cuándo ocurrió la búsqueda?
  • ¿Quién ejecutó la búsqueda?
  • ¿Cuál es el contenido de la búsqueda?

Debemos ajustar las configuraciones de auditoría, los valores predeterminados son bastante amplios:

  1. Habilitar log de auditoría de seguridad: Configura xpack.security.audit.enabled: true en tu elasticsearch.yml.
  2. Habilitar log o índice en las salidas de auditoría de seguridad: Configura xpack.security.audit.outputs:[logfile, index] en tu elasticsearch.yml.

    Notas:
    • La configuración xpack.security.audit.outputs solo aplica en las versiones 6.0-6.2 y 5.x. La versión 7.0 ya no acepta esta configuración y configura de forma predeterminada la salida en json (<nombre del cluster>_audit.json) cuando xpack.security.audit.enabled está configurada como true.
    • Para fines de la solución de problemas, recomendamos elegir el archivo de log por sobre el índice dado que el nivel de detalle de los logs de auditoría pueden introducir estrés no deseado en el rendimiento del cluster en el cual el índice de seguridad sobrepase su tamaño previsto.
    • El modo de auditoría puede ser muy detallado, considera desactivarlo cuando termines de solucionar el problema.
  3. Incluye el acceso a authentication_success en tu lista de eventos: Configura xpack.security.audit.logfile.events.include: authentication_success en tu elasticsearch.yml.

    Notas:
    • Esta configuración no está incluida en los eventos predeterminados. Si la configuras, se sobrescribirán los valores predeterminados.
    • Si necesitas agregar un evento más (no reemplazarlo), escribe primero la lista de eventos predeterminada existente y luego agrega la configuración anterior después de la última entrada.
    • Emite la salida del cuerpo de la solicitud en los eventos de auditoría: Configura xpack.security.audit.logfile.events.emit_request_body: true en tu elasticsearch.yml.

Con estas configuraciones, puedes monitorear la búsqueda del usuario de esta forma.

  • Usuario: louisong
  • Tiempo de búsqueda: 2019-05-01T19:26:53,554 (UTC)
  • Endpoint de búsqueda: _msearch (generalmente significa que es una búsqueda emitida desde un dashboard/visualización de Kibana)
  • Cuerpo de la búsqueda: empieza desde "request.body": en el log siguiente:

    {"@timestamp":"2019-05-01T19:26:53,554", "node.id":"Z1z_64sIRcy4dW2eqyqzMw", "event.type":"rest", "event.action":"authentication_success", "user.name":"louisong", "origin.type":"rest", "origin.address":"127.0.0.1:51426", "realm":"default_native", "url.path":"/_msearch", "url.query":"rest_total_hits_as_int=true&ignore_throttled=true", "request.method":"POST", "request.body":"{\"index\":\"*\",\"ignore_unavailable\":true,\"preference\":1556709820160}\n{\"aggs\":{\"2\":{\"terms\":{\"field\":\"actions\",\"size\":5,\"order\":{\"_count\":\"desc\"},\"missing\":\"__missing__\"}}},\"size\":0,\"_source\":{\"excludes\":[]},\"stored_fields\":[\"*\"],\"script_fields\":{},\"docvalue_fields\":[{\"field\":\"access_token.user_token.expiration_time\",\"format\":\"date_time\"},{\"field\":\"canvas-workpad.@created\",\"format\":\"date_time\"},{\"field\":\"canvas-workpad.@timestamp\",\"format\":\"date_time\"},{\"field\":\"creation_time\",\"format\":\"date_time\"},{\"field\":\"expiration_time\",\"format\":\"date_time\"},{\"field\":\"maps-telemetry.timeCaptured\",\"format\":\"date_time\"},{\"field\":\"task.runAt\",\"format\":\"date_time\"},{\"field\":\"task.scheduledAt\",\"format\":\"date_time\"},{\"field\":\"updated_at\",\"format\":\"date_time\"},{\"field\":\"url.accessDate\",\"format\":\"date_time\"},{\"field\":\"url.createDate\",\"format\":\"date_time\"}],\"query\":{\"bool\":{\"must\":[{\"range\":{\"canvas-workpad.@timestamp\":{\"format\":\"strict_date_optional_time\",\"gte\":\"2019-05-01T11:11:53.498Z\",\"lte\":\"2019-05-01T11:26:53.498Z\"}}}],\"filter\":[{\"match_all\":{}},{\"match_all\":{}}],\"should\":[],\"must_not\":[]}},\"timeout\":\"30000ms\"}\n", "request.id":"qrktsPxyST2nVh29GG7tog"}
        

    Conclusión

    En este artículo, analizamos las causas comunes de las búsquedas lentas y las soluciones para abordarlas. También vimos los distintos métodos para identificar búsquedas lentas constantes y ocasionales. Es habitual que las búsquedas lentas sean un síntoma de un problema de rendimiento mayor del cluster que debe solucionarse.

    En Elasticsearch, buscamos constantemente mejorar los tiempos de búsquedas y trabajamos para lograr un rendimiento de búsquedas más rápido en nuestras futuras versiones. Esperamos que este artículo te haya resultado útil para enfrentarte a búsquedas lentas. No dudes en publicar preguntas en nuestro foro de discusión de Elasticsearch para un análisis más detallado. ¡Feliz búsqueda!