Optimización del uso de memoria de Elasticsearch para un rendimiento máximo

Domine la gestión de memoria de Elasticsearch para obtener el máximo rendimiento. Esta guía explora técnicas vitales que incluyen el dimensionamiento del heap de JVM, la optimización de la indexación y la búsqueda, el aprovechamiento del almacenamiento en caché y el despliegue de disyuntores (circuit breakers) para prevenir errores OutOfMemory. Aprenda estrategias prácticas para asegurar que su clúster de Elasticsearch permanezca estable y receptivo, incluso bajo una carga pesada.

Optimización del uso de memoria de Elasticsearch para un rendimiento máximo

Los problemas de memoria de Elasticsearch suelen manifestarse como búsquedas lentas, pausas largas de recolección de basura, errores de interruptores de circuito o nodos que abandonan el clúster. Optimizar el uso de memoria de Elasticsearch implica equilibrar el heap de JVM, la caché del sistema de archivos, la cantidad de fragmentos, el comportamiento de las consultas y la presión de indexación, en lugar de solo aumentar -Xmx.

El objetivo es simple: proporcionar a Elasticsearch suficiente heap para el trabajo del clúster y las consultas, dejando suficiente RAM para que el sistema operativo almacene en caché los archivos de segmento de Lucene.

Comprender los componentes de memoria de Elasticsearch

Elasticsearch utiliza memoria en dos áreas principales:

  • Heap de JVM: Contiene metadatos del clúster, búferes de indexación, estructuras de consultas, datos de campo cuando están habilitados, cachés y otros objetos de Java. Demasiado poco heap causa presión y activaciones del interruptor. Demasiado heap puede alargar la recolección de basura y privar a la caché del sistema de archivos.
  • Caché del sistema de archivos y memoria nativa: El sistema operativo almacena en caché los archivos de índice de Lucene fuera del heap de JVM. Elasticsearch también utiliza memoria nativa para redes, pilas de hilos y archivos mapeados en memoria.

Configurar el tamaño del heap de JVM

El tamaño del heap es la primera configuración que debes verificar. Elasticsearch utiliza archivos jvm.options u opciones de JVM específicas del entorno, dependiendo de cómo se instaló.

Establecer Xms y Xmx juntos

Establece -Xms y -Xmx al mismo valor para que la JVM no redimensione el heap mientras el nodo está en ejecución.

Como regla general, mantén el heap en o por debajo de aproximadamente la mitad de la RAM física y evita cruzar el umbral del puntero de objeto ordinario comprimido. En la práctica, muchos nodos de producción se mantienen por debajo de aproximadamente 30 GB de heap, pero debes verificar el umbral exacto y la orientación para tu versión de Elasticsearch y JVM.

Por ejemplo:

-Xms4g
-Xmx4g

Esto establece el heap inicial y máximo en 4 GB.

Monitorear el uso del heap

Utiliza Kibana Stack Monitoring, exportadores de Prometheus o la API de estadísticas de nodos:

curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

Observa heap_used_percent, el tiempo de recolección de basura, la presión de la generación anterior y las activaciones del interruptor de circuito. Un heap que se mantiene alto durante largos períodos después de la recolección de basura generalmente significa que necesitas reducir los consumidores de heap o agregar capacidad.

Reducir la presión de memoria de fragmentos y consultas

El diseño del índice y la forma de la consulta tienen un efecto directo en la memoria.

Tamaño y cantidad de fragmentos

Cada fragmento tiene sobrecarga. Demasiados fragmentos pequeños desperdician heap y ralentizan las operaciones del clúster. Los fragmentos muy grandes pueden hacer que la recuperación y reubicación sean difíciles. Muchos clústeres funcionan bien con tamaños de fragmento en decenas de gigabytes, pero los registros, datos de series temporales e índices con muchas búsquedas pueden necesitar objetivos diferentes.

Por ejemplo, si un índice de registro diario crea 30 fragmentos primarios para 20 GB de datos, estás pagando sobrecarga por muchos fragmentos pequeños. Uno o dos primarios pueden ser más fáciles de gestionar, dependiendo de la retención y los patrones de consulta.

Fusión de segmentos

Elasticsearch utiliza segmentos de Lucene para la indexación. Los segmentos más pequeños se fusionan en otros más grandes con el tiempo. Este proceso puede consumir mucha memoria. Aunque Elasticsearch maneja la fusión automáticamente, comprender su impacto puede ser beneficioso, especialmente durante cargas de indexación pesadas.

Optimización de búsqueda y agregación

  • Usa campos de tipo keyword para agregaciones: Agrega y ordena en campos keyword, numéricos, de fecha u otros respaldados por valores de documento. Evita habilitar fielddata en campos text grandes a menos que entiendas el costo de heap.
  • Limita las consultas costosas: Los comodines iniciales y las consultas de expresiones regulares amplias pueden ser costosas. Prefiere campos estructurados, prefijos, n-gramas o mapeos de búsqueda mientras escribes cuando el caso de uso necesita coincidencias parciales.
  • Perfila las búsquedas lentas: Usa la API de perfil en un entorno de prueba para encontrar las cláusulas de consulta que generan más trabajo.

Usar las cachés deliberadamente

Elasticsearch tiene múltiples cachés. Ayudan con el trabajo repetido, pero también consumen memoria.

  • Caché de solicitudes de fragmento: Almacena en caché los resultados de búsqueda a nivel de fragmento para solicitudes elegibles, a menudo útil para consultas repetidas de tipo agregación en datos mayormente sin cambios. Su tamaño se controla con:

    indices.requests.cache.size: 5%
    

    Este ejemplo establece el tamaño de la caché de solicitudes de fragmento en el 5% del heap.

  • Caché de consultas de nodo: Almacena en caché los resultados del contexto de filtro. Su tamaño se controla por separado:

    indices.queries.cache.size: 10%
    
  • Caché de datos de campo: Consume heap y puede crecer rápidamente si habilitas fielddata en campos de texto. Prefiere mapear los campos correctamente en lugar de depender de una caché de datos de campo más grande.

Prevenir errores de falta de memoria

Los errores de falta de memoria suelen ser el resultado final de una presión sostenida. La solución rara vez es "aumentar todos los límites".

Tratar la recolección de basura como un síntoma

Las versiones recientes de Elasticsearch eligen los valores predeterminados de JVM compatibles por ti. Evita la personalización del recolector de basura a menos que tengas orientación y mediciones específicas de la versión. Las pausas largas generalmente apuntan a un exceso de fragmentación, agregaciones costosas, datos de campo, demasiada presión de heap o nodos insuficientes.

Los indicadores clave de problemas de GC incluyen:

  • Alto tiempo de GC.
  • Pausas largas de detener el mundo.
  • Uso de heap que vuelve a subir cerca del límite después de cada recolección.
  • Errores OOM durante búsquedas grandes, indexación masiva o agregaciones.

Respetar los interruptores de circuito

Los interruptores de circuito estiman el uso de memoria y rechazan operaciones antes de que puedan agotar el nodo.

  • Interruptor de datos de campo: Limita el heap utilizado para fielddata.
  • Interruptor de solicitud: Limita la memoria utilizada para completar las estructuras de datos de la solicitud.
  • Interruptor principal: Realiza un seguimiento de las estimaciones combinadas del interruptor.

Ver las estadísticas del interruptor con:

curl -X GET "localhost:9200/_nodes/stats/breaker?pretty"

Puedes cambiar algunas configuraciones del interruptor a través de la configuración del clúster, pero hazlo solo después de saber por qué se está activando el interruptor. Un interruptor activado a menudo está protegiendo al nodo de un OOM.

Monitorear y alertar

Alerta sobre:

  • Uso de heap de JVM después de la recolección de basura.
  • Tiempo de recolección de basura y pausas largas.
  • Activaciones del interruptor de circuito.
  • Presión de indexación y tareas de grupo de hilos rechazadas.
  • Presión de memoria del SO y uso de intercambio.
  • Cantidad de fragmentos por nodo y agregaciones inusualmente grandes.

Conclusión

Comienza con el tamaño del heap, luego observa la cantidad de fragmentos, los mapeos de campos, las agregaciones grandes y las activaciones repetidas del interruptor de circuito. Si tu nodo todavía está bajo presión después de la limpieza, agrega capacidad o divide las cargas de trabajo en lugar de ocultar las señales de advertencia con límites más grandes.