ElasticSearch: El problema del oversharding

ElasticSearch es un poderoso motor de búsqueda basando en Apache Lucene, en este blog hemos hablado largo y tendido de esta tecnología, incluso tengo un curso creado en Youtube del tema. Sin embargo no hemos tocado temas profundos de esta herramienta. En esta entrada hablaremos un poco de tuning y sharding, tocaremos el error más común que se comete en ES, el over-sharding.

Sharding, la gallina de los huevos de oro de ElasticSearch

El sharding o fragmentación es un patrón empleado por los sistemas informáticos en general y por los de bases de datos en particular para dividir horizontal o verticalmente la información. ES en particular emplea fragmentación horizontal.

Para que se comprenda mejor, el sharding es la técnica de dividir una colección de documentos en ES en distribuciones separadas para agilizar los procesos de indexación y recuperación de información. ElasticSearch funciona tan bien (en lectura) por ser un sistema de distribuido aprovechando los shards y los índices invertidos de Lucene. 

El sharding gráficamente lo podemos ver en el siguiente gráfico.

La fragmentación se basa en 3 estrategias fundamentales: búsqueda, de rango, y basada en hash. Esta última estrategia permite una distribución uniforme de los datos en los shards. En nuestro libro de microservicios, hablamos de este y otros patrones.

Visto lo anterior pudíeramos pensar que si separamos una colección de documentos de ElasticSearch (Llamado índice) en muchos shards el rendimiento mejoraría, tanto en los procesos de indexación como de búsqueda, y la realidad es que todo en exceso es malo.

Over-sharding, el error más común en ES

Uno de los errores más comunes es crear demasiados shards asociados a un índice. Este error tiene un mayor impacto cuando ES se ejecuta sobre un solo nodo, el problema se pone en práctica en los procesos de indexación y consulta. Cuando una de estas operaciones tiene lugar se pone en acción una de las ventajas más poderosas de ES, su funcionamiento distribuido; lo que pasa es que cuando todo está en un mismo nodo y buscamos sobre un índice ES realizará de forma paralela el proceso de búsqueda y lo que sucederá es que todos los shards competirán por los mismos recursos a la vez (CPU, RAM, IO) degradando los recursos y aumentando los tiempos de respuesta.

Cuando el número de peticiones comienza a subir el problema se vuelve exponencial y se degrada todo el sistema. En las operaciones de escritura (e indexación) este problema se ve especialmente agudizado puesto que es un proceso más costoso computacionalmente (por el uso de índices invertidos en Lucene), unido a esto una mala praxis de uno usar la Bulk API complejizará el problema.

La definición de un índice ideal es muy difícil, sin embargo hay algunos tips que pueden ser considerados:

  • Mantener los datos proporcionales en los fragmentos, y los nodos donde se encuentren los shards con características similares de hardware que garanticen un rendimiento similar.
  • Evitar tener muchos shards en el mismo nodo (hardware), el tener muchos fragmentos en el mismo nodo se crearán cuellos de botella al todos competir por los mismos recursos a la hora de realizar una tarea. Muchos fragmentos en un único nodo afectarán el rendimiento en lugar mejorarlo.
  • El tamaño de un shard máximo debe ser de 20-40GB. Evitar shards de tamaño pequeño, los shards de tamaño superior a 1GB ofrecen mejor rendimiento.
  • Implementar políticas de gestión del ciclo de vida de un índice (ILM, rollover), en otra entrada abordaremos este tema.
  • Distribuir los shards en nodos distintos para evitar la competencia por los recursos en el mismo nodo.

Los creadores de ES tienen un apartado dedica a este tema, puedes ver más acá.