¿Cuántos shards debo tener en mi cluster de Elasticsearch?

Nota del editor: La regla general sobre "tener como objetivo 20 shards o menos por GB de memoria heap" es obsoleta a partir de la versión 8.3. Se actualizó este blog para reflejar la nueva recomendación.

Elasticsearch es una plataforma muy versátil que soporta varios casos de uso y brinda gran flexibilidad para la organización de datos y las estrategias de replicación. Sin embargo, en ocasiones esta flexibilidad puede dificultar determinar por anticipado cómo organizar de la mejor manera tus datos en índices y shards, en especial si eres nuevo en el Elastic Stack. Si bien las elecciones subóptimas no necesariamente generarán problemas al inicio, tienen el potencial de causar problemas de rendimiento a medida que aumentan los volúmenes de datos con el tiempo. Mientras más datos contiene el cluster, más difícil resulta corregir el problema. Esto se debe a que, a veces, puede ser necesario reindexar grandes cantidades de datos.

Cuando encontramos usuarios que tienen problemas de rendimiento, con frecuencia es posible rastrear su origen hasta inconvenientes en cómo se indexan los datos y en la cantidad de shards en el cluster. Esto suele ser así, en especial, en los casos de uso que involucran multitenancy o el uso de índices basados en el tiempo. Al hablar de este tema con los usuarios, tanto personalmente en eventos o reuniones como a través de nuestro foro, algunas de las preguntas más comunes son: "¿cuántos shards debería tener?" y "¿qué tan grandes deberían ser los shards?".

El objetivo de este blog es ayudarte a responder estas preguntas y brindar una orientación práctica para los casos de uso que involucrar el uso de índices basados en el tiempo (por ejemplo, logging o analítica de seguridad) en un solo lugar.

¿Qué es un shard?

Antes de comenzar, debemos establecer algunas cuestiones y terminología que necesitaremos en secciones posteriores.

Los datos en Elasticsearch se organizan en índices. Cada índice se compone de uno o más shards. Cada shard es una instancia de un índice de Lucene, en lo que puedes pensar como un motor de búsqueda autocontenido que indexa y se encarga de las búsquedas de un subconjunto de datos en un cluster de Elasticsearch.

A medida que los datos se escriben en un shard, se publican periódicamente en nuevos segmentos inmutables de Lucene en disco, y entonces se vuelven disponibles para la búsqueda. A esto se lo denomina "actualización". El funcionamiento se describe en más detalle en Elasticsearch: the Definitive Guide (Elasticsearch: La guía definitiva).

A medida que aumenta la cantidad de segmentos, estos se consolidan periódicamente en segmentos más grandes. A este proceso se lo denomina "fusión". Dado que todos los segmentos son inmutables, esto significa que el espacio en disco usado generalmente fluctuará durante la indexación debido a que deben crearse segmentos nuevos fusionados para poder eliminar aquellos a los que reemplazarán. La fusión puede consumir muchos recursos, en especial con respecto a la E/S del disco.

El shard es la unidad en la que Elasticsearch distribuye los datos en el cluster. La velocidad a la que Elasticsearch puede mover los shards al rebalancear los datos (por ejemplo, tras un fallo) dependerá del tamaño y la cantidad de shards, y del rendimiento del disco y la red.

SUGERENCIA: Evita tener shards muy grandes, dado que esto puede afectar de forma negativa la capacidad del cluster de recuperarse ante un fallo. No existe un límite fijo de tamaño de los shards, pero un tamaño de shard de 50 GB suele citarse como un límite que funciona en una variedad de casos de uso.

Índice por período de retención

Como los segmentos son inmutables, actualizar un documento requiere que Elasticsearch primero encuentre el documento existente, luego lo marque como eliminado y agregue la versión actualizada. Eliminar un documento también requiere que el documento se encuentre y se marque como eliminado. Por este motivo, los documentos eliminados seguirán usando espacio en disco y algunos recursos del sistema hasta que se fusionen, lo cual puede consumir muchos recursos del sistema.

Elasticsearch permite una eliminación muy eficiente de índices completos directamente desde el sistema de archivos, sin tener que eliminar de manera explícita todos los registros individualmente. Es, por mucho, la forma más eficiente de eliminar datos de Elasticsearch.


SUGERENCIA: Intenta usar índices basados en el tiempo para gestionar la retención de datos cuando sea posible. Agrupa los datos en índices según el período de retención. Los índices basados en el tiempo también facilitan variar la cantidad de shards primarios y réplicas en el tiempo, dado que esto puede cambiarse en el próximo índice que se generará. Esto simplifica la adaptación a los requisitos y los volúmenes de datos cambiantes.


¿Los índices y shards no son gratis?

De cada índice de Elasticsearch, la información sobre mapeos y estado se almacena en el estado del cluster. Esto se mantiene en memoria para poder acceder con rapidez. Tener una gran cantidad de índices y shards en un cluster, por lo tanto, puede dar como resultado un estado de cluster grande, en especial si los mapeos son grandes. Es posible que actualizar esto sea lento, debido a que todas las actualizaciones deben hacerse a través de un solo thread a fin de garantizar la consistencia antes de que se distribuyan los cambios por todo el cluster.


SUGERENCIA: A fin de reducir la cantidad de índices y evitar mapeos grandes y crecientes, considera almacenar los datos con estructura similar en el mismo índice, en lugar de separarlos en diferentes índices según el origen de los datos. Es importante encontrar un buen equilibrio entre la cantidad de índices y shards, y el tamaño de mapeo de cada índice individual. Como el estado del cluster se carga en la memoria heap de cada nodo (incluidos los maestros) y la cantidad de memoria heap es directamente proporcional a la cantidad de índices, campos por índice y shards, es importante monitorear también el uso de la memoria heap en los nodos maestros y asegurarse de que se dimensionen correctamente.


Cada shard tiene datos que deben mantenerse en memoria y usan espacio de la memoria heap. Esto incluye estructuras de datos que mantienen información a nivel del shard, pero también a nivel del segmento para definir dónde residen los datos en el disco. El tamaño de estas estructuras de datos no es fijo y variará según el caso de uso.

Una característica importante de la sobrecarga relacionada con los segmentos, sin embargo, es que no es estrictamente proporcional al tamaño del segmento. Esto significa que los segmentos más grandes tienen menos sobrecarga por volumen de datos en comparación con segmentos más pequeños. La diferencia puede ser significativa.

A fin de poder almacenar la mayor cantidad de datos posibles por nodo, resulta importante gestionar el uso de memoria heap y reducir lo más posible la cantidad de sobrecarga. Mientras más espacio de memoria heap tenga un nodo, más datos y shards podrá controlar.

Por lo tanto, los índices y shards no son gratis desde la perspectiva del cluster, dado que hay una cierta sobrecarga de recursos por cada índice y shard.


SUGERENCIA: Los shards pequeños dan como resultado segmentos pequeños, lo cual aumenta la sobrecarga. Intenta mantener el tamaño promedio de shard entre algunos GB y algunas decenas de GB. En los casos de uso con datos basados en el tiempo, es habitual ver shards de entre 20 GB y 40GB.

SUGERENCIA: Como la sobrecarga por shard depende del recuento y el tamaño de los segmentos, forzar la fusión de segmentos más pequeños en otros más grandes con la operación forcemerge puede reducir la sobrecarga y mejorar el rendimiento de la búsqueda. Idealmente, esto debería realizarse una vez que no se escriban más datos en el índice. Ten en cuenta que es una operación costosa y lo ideal sería realizarla en las horas con menor demanda.

SUGERENCIA: La cantidad de shards que puedes tener en un nodo será proporcional a la cantidad de memoria heap que tengas disponible, pero no hay un límite fijo impuesto por Elasticsearch. Una buena regla general es asegurarse de mantener menos de 20 shards por nodo por GB de memoria heap configurado. Un nodo con una memoria heap de 30 GB, por lo tanto, debería tener un máximo de 600 shards, pero mientras más alejado puedas mantenerte por debajo de este límite, mejor. En general, esto ayudará a mantener el cluster en buen estado. (Nota del editor: A partir de la versión 8.3, redujimos en gran medida el uso de memoria heap por shard, por lo que actualizamos la regla general en este blog. Ve la SUGERENCIA siguiente para las versiones 8.3 y posteriores de Elasticsearch).

NUEVA SUGERENCIA: Permitir 1 kB de memoria heap por campo por índice en los nodos de datos, más las sobrecargas
El uso exacto de recursos de cada campo mapeado depende de su tipo, pero una regla general es permitir aproximadamente 1 kB de sobrecarga de memoria heap por campo mapeado por índice mantenido en cada nodo de datos. También debes permitir suficiente memoria heap para el uso básico de Elasticsearch y tu carga de trabajo, como indexación, búsquedas y agregaciones. Una memoria heap adicional de 0.5 GB será suficiente para muchas cargas de trabajo razonables, y es posible que necesites incluso menos si tu carga de trabajo es muy liviana, mientras que una carga más pesada requerirá más.

Por ejemplo, si un nodo de datos mantiene shards de 1000 índices, cada uno con 4000 campos mapeados, entonces deberías permitir aproximadamente 1000 × 4000 × 1kB = 4 GB de memoria heap para los campos y otros 0.5 GB para la carga de trabajo y otras sobrecargas; entonces, este nodo necesitará un tamaño de memoria heap de al menos 4.5 GB.


¿Cómo afecta el tamaño del shard al rendimiento?

En Elasticsearch, cada búsqueda se ejecuta en un solo thread por shard. Sin embargo, varios shards pueden procesarse en paralelo, al igual que pueden hacerlo varias búsquedas y agregaciones en el mismo shard.

Esto significa que la latencia de búsqueda mínima, cuando no se involucra el almacenamiento en caché, dependerá de los datos, del tipo de búsqueda y del tamaño del shard. Buscar en muchos shards pequeños acelerará el procesamiento por shard, pero dado que muchas más tareas se pondrán en cola y procesarán en secuencia, no será necesariamente más rápido que buscar en una menor cantidad de shards más grandes. Tener muchos shards pequeños también puede reducir el rendimiento de la búsqueda si hay varias búsquedas concurrentes.


SUGERENCIA: La mejor forma de determinar el tamaño máximo de los shards desde una perspectiva de rendimiento de la búsqueda es una evaluación comparativa con búsquedas y datos realistas. Realiza siempre la evaluación comparativa con una búsqueda y carga de indexación representativas de lo que el nodo tendría que manejar en producción, debido a que optimizar para una sola búsqueda puede llevar a resultados engañosos.


¿Cómo gestiono el tamaño de los shards?

Al usar índices basados en el tiempo, cada índice se ha asociado tradicionalmente con un período de tiempo fijo. Los índices diarios son muy comunes y suelen usarse para mantener datos con período de retención breve o grandes volúmenes diarios. Permiten que el período de retención se gestione con buena granularidad y facilitan los ajustes para volúmenes cambiantes a diario. Los datos con un período de retención más prolongado, en especial si los volúmenes diarios no garantizan el uso de índices diarios, suelen usar índices semanales o mensuales para mantener elevado el tamaño de los shards. Esto reduce la cantidad de índices y shards que deben almacenarse en el cluster con el tiempo.


SUGERENCIA: Si usas índices basados en el tiempo que abarcan un período fijo, ajusta el período que cubre cada índice según el período de retención y los volúmenes de datos esperados para alcanzar el tamaño de shard objetivo.


Los índices basados en el tiempo con un intervalo de tiempo fijo funcionan bien cuando los volúmenes de datos son razonablemente predecibles y cambian lento. Si la tasa de indexación puede variar rápidamente, es muy difícil mantener un tamaño de shard objetivo uniforme.

Para poder manejar mejor este tipo de escenarios, se introdujeron las API de rollover y reducción. Agregan mucha flexibilidad a la gestión de índices y shards, en especial a los índices basados en el tiempo.

La API de índice de rollover posibilita especificar la cantidad de documentos que debería contener un índice o el período máximo durante el cual deberían escribirse documentos en él. Una vez superado uno de estos criterios, Elasticsearch puede activar la creación de un índice nuevo para la escritura sin tiempo de inactividad. En lugar de que cada índice cubra un período de tiempo específico, ahora es posible cambiar a un índice nuevo al alcanzar un tamaño específico, lo cual facilita tener un tamaño de shards más parejo en todos los índices.

En casos en los que puedan actualizarse los datos, ya no hay un enlace distinto entre la marca de tiempo del evento y el índice en el que reside al usar esta API, lo cual puede hacer que las actualizaciones sean significativamente menos eficientes dado que cada actualización puede necesitar estar precedida por una búsqueda.


SUGERENCIA: Si tienes datos inmutables basados en el tiempo en los que los volúmenes pueden variar significativamente con el tiempo, considera usar la API de índice de rollover para lograr un tamaño de shard objetivo óptimo variando de forma dinámica el período de tiempo que abarca cada índice. Esto brinda gran flexibilidad y puede ayudar a evitar tener shards demasiado grandes o pequeños cuando los volúmenes de datos son impredecibles.


La API de índice de reducción te permite reducir un índice existente a un índice nuevo con menos shards primarios. Si se desea una distribución pareja de los shards en los nodos durante la indexación, pero esto dará como resultado shards demasiado pequeños, esta API se puede usar para reducir la cantidad de shards primarios una vez que ya no se realicen indexaciones en el índice. El resultado será shards más grandes, más adecuados para el almacenamiento de datos a largo plazo.


SUGERENCIA: Si necesitas que cada índice cubra un período de tiempo específico, pero deseas poder distribuir la indexación en una gran cantidad de nodos, considera usar la API de reducción para disminuir la cantidad de shards primarios una vez que ya no se realicen indexaciones en el índice. Esta API también se puede usar para disminuir la cantidad de shards en caso de que hayas configurado demasiados shards al inicio.


Conclusiones

En este blog se proporcionaron consejos y orientación práctica sobre cómo gestionar los datos en Elasticsearch de la mejor manera. Si te interesa conocer más, "Elasticsearch: the definitive guide" (Elasticsearch: La guía definitiva) contiene una sección sobre diseñar para escalar, que vale la pena leer a pesar de que es un poco antigua.

Muchas decisiones respecto a cómo distribuir mejor tus datos en los índices y shards dependerán de las especificaciones de los casos de uso, y a veces puede ser difícil determinar cómo aplicar mejor el consejo disponible. Para obtener consejos más detallados y personalizados, involúcrate con nosotros comercialmente a través de una suscripción y deja que nuestros equipos de Soporte y Consultoría te ayuden a acelerar tu proyecto. Si deseas debatir sobre tu caso de uso de forma pública, también puedes obtener ayuda de nuestra comunidad y a través del foro público.


Este blog se publicó originalmente el 18 de septiembre de 2017. Se actualizó el 6 de julio de 2022.