Comprendre et régler la taille du tas JVM d'Elasticsearch pour les performances

Guide pratique pour dimensionner le tas JVM d'Elasticsearch, interpréter les symptômes GC et éviter les réglages mémoire qui nuisent aux performances de recherche.

Comprendre et régler la taille du tas JVM d'Elasticsearch pour les performances

La taille du tas JVM d'Elasticsearch est l'un de ces paramètres que les gens touchent souvent trop tôt et blâment trop tard. Un cluster lent n'a pas toujours besoin de plus de tas. Parfois, c'est l'inverse : le tas est suffisamment grand, mais le système d'exploitation a trop peu de mémoire restante pour le cache du système de fichiers, ce qui oblige Lucene à revenir plus souvent sur le disque. D'autres fois, le tas est vraiment trop petit, le garbage collection s'exécute constamment, et chaque recherche semble avancer dans du ciment humide.

L'objectif pratique n'est pas de trouver un nombre magique. L'objectif est de donner à Elasticsearch suffisamment de tas pour les métadonnées du cluster, les tampons d'indexation, les agrégations, le travail de requête et les caches, tout en laissant suffisamment de RAM en dehors du tas pour les fichiers de segments Lucene et le système d'exploitation. Si vous ne retenez qu'une seule chose, retenez que les performances d'Elasticsearch dépendent à la fois de la mémoire dans le tas et en dehors.

Commencez par séparer deux types de pression mémoire. La pression sur le tas JVM se manifeste par un heap.percent élevé, de longues pauses de garbage collection, des exceptions du coupe-circuit parent, une pression fielddata, ou une OutOfMemoryError. La pression hors tas se traduit généralement par des recherches lentes même si le tas semble correct, des lectures disque élevées, de l'activité de swap, ou de mauvais taux de succès de cache après un redémarrage. Augmenter le tas peut aider dans le premier cas. Cela peut aggraver le second cas.

Pour la plupart des clusters auto-gérés, l'ancienne règle empirique fonctionne encore comme point de départ : définissez le tas à la moitié au maximum de la RAM de la machine, et maintenez-le en dessous du seuil des pointeurs d'objet compressés. Les gens citent souvent ce seuil comme "environ 32 Go". En pratique, de nombreux opérateurs restent autour de 26-31 Go car le seuil exact dépend de la JVM et de la disposition à l'exécution. Elasticsearch enregistre si les pointeurs d'objet compressés sont activés au démarrage. Considérez le journal de démarrage comme la source de vérité pour votre nœud.

Sur les versions modernes d'Elasticsearch, le dimensionnement automatique du tas peut déjà définir une valeur raisonnable en fonction des rôles des nœuds et de la mémoire disponible. C'est utile, surtout pour les petits clusters et les déploiements standard. Le réglage manuel reste important lorsqu'un nœud a une charge de travail inhabituelle : agrégations lourdes, mappings volumineux, nombreux shards, pipelines d'ingestion, tâches de transformation, rôles d'apprentissage automatique, ou un mélange de recherche à chaud et de volume d'indexation élevé.

Voici un exemple simple. Supposons que vous ayez un nœud de données chaud de 64 Go qui gère à la fois l'indexation et la recherche. Un tas de 30 Go est un point de départ courant. Il laisse environ la moitié de la RAM pour le cache de pages du système d'exploitation et la mémoire native. Si le même nœud fait partie d'un cluster de journalisation avec de nombreux petits shards et des agrégations à haute cardinalité, vous pourriez encore voir une pression sur le tas. La solution pourrait être une meilleure conception des shards ou un nettoyage du mapping, pas automatiquement un tas de 40 Go. Dépasser le seuil des pointeurs compressés peut augmenter la taille des pointeurs d'objet et réduire l'efficacité effective du tas.

Définissez le tas minimum et maximum à la même valeur. Elasticsearch ne devrait pas passer du temps à redimensionner le tas pendant qu'il sert le trafic.

-Xms30g
-Xmx30g

Utilisez un fichier sous jvm.options.d/ plutôt que de modifier directement le fichier jvm.options fourni lorsque votre installation le supporte. Pour les installations par paquet, cela signifie généralement quelque chose comme /etc/elasticsearch/jvm.options.d/heap.options. Pour Docker, transmettez les paramètres du tas via la variable d'environnement prise en charge ou la configuration montée. Gardez le paramètre cohérent pour les nœuds ayant le même rôle, mais ne supposez pas que chaque rôle a besoin du même tas. Les nœuds éligibles maîtres dédiés ont souvent besoin de beaucoup moins de tas que les nœuds de données occupés, sauf si le cluster a de très grandes métadonnées en raison d'un trop grand nombre d'index, de champs ou de shards.

Avant de modifier le tas, prenez un instantané du comportement actuel. Regardez les statistiques JVM :

GET _nodes/stats/jvm?filter_path=nodes.*.jvm.mem,nodes.*.jvm.gc

Ensuite, vérifiez les coupe-circuits et fielddata :

GET _nodes/stats/breaker,indices/fielddata?pretty

Vérifiez également les shards et les mappings. Un cluster avec des milliers de minuscules shards peut brûler du tas en frais généraux même lorsque le volume de données n'est pas énorme.

GET _cat/shards?v&bytes=gb
GET _cluster/stats?filter_path=indices.count,indices.shards,indices.mappings

Les symptômes comptent plus que le chiffre principal. Un tas autour de 70-85% lors des pics peut être normal si le garbage collection le réduit rapidement. Un tas qui monte dans les hauts 90% et y reste est différent. Les longues pauses GC de l'ancienne génération sont pires qu'un pourcentage élevé en soi. Si les recherches expirent chaque fois que le GC s'exécute, les utilisateurs ne se soucient pas que le graphique moyen du tas semblait acceptable.

Une erreur courante en production est d'utiliser le tas comme un pansement pour de mauvais mappings. Trier ou agréger sur des champs text analysés avec fielddata: true peut consommer beaucoup de tas. La meilleure solution est généralement un sous-champ keyword, un champ à cardinalité plus faible, ou une conception d'agrégation différente. Une autre erreur est de permettre aux mappings dynamiques de créer des milliers de champs à partir de clés JSON arbitraires. Le tas disparaît alors dans les mappings, l'état du cluster et les structures de requête. Mettez des limites aux champs dynamiques avant qu'ils ne deviennent un problème opérationnel.

Les agrégations méritent une attention particulière. Une agrégation terms sur un champ à haute cardinalité peut créer de grandes structures en mémoire. Si quelqu'un exécute un tableau de bord qui regroupe par user_id, session_id ou URL complète sur une longue plage de temps, la pression sur le tas peut augmenter même si les recherches ordinaires semblent correctes. Utilisez des fenêtres de temps plus petites, des filtres qui réduisent l'ensemble de travail, l'agrégation composite pour la pagination, ou des rollups pré-agrégés lorsque c'est approprié. Augmenter le tas peut retarder l'échec, mais cela ne rendra pas une agrégation illimitée bon marché.

L'indexation a son propre modèle de tas. Les requêtes en masse créent une pression transitoire. Des charges utiles de masse très volumineuses peuvent forcer Elasticsearch à retenir trop de travail à la fois. Si vous voyez des rejets d'indexation ou des pics de tas pendant l'ingestion, essayez des lots de masse plus petits et un contrôle de concurrence côté client plus important. Une plage de départ utile est souvent de quelques mégaoctets par requête en masse, puis testez à la hausse avec vos documents réels. La bonne valeur dépend de la taille du document, des pipelines d'ingestion, des réplicas, de l'intervalle d'actualisation et de la vitesse de stockage.

N'ignorez pas le système d'exploitation. Désactivez le swap ou configurez l'hôte pour que la mémoire d'Elasticsearch ne soit pas échangée. L'échange peut transformer un problème mémoire récupérable en une longue panne car les pauses JVM deviennent énormes. Assurez-vous que le processus a la permission de verrouiller la mémoire si vous utilisez bootstrap.memory_lock: true, et vérifiez-le au démarrage plutôt que de supposer que le paramètre a fonctionné.

Après avoir modifié le tas, redémarrez un nœud à la fois dans un cluster de production et attendez que la récupération des shards se stabilise avant de passer au nœud suivant. Surveillez la latence de recherche, les pauses GC, les lectures disque et le débit d'indexation pendant au moins un cycle de trafic normal. Un changement qui semble bon pendant une heure calme peut échouer lors de la ruée matinale des tableaux de bord.

Si un nœud continue de subir une pression sur le tas après un réglage raisonnable, reculez et demandez ce que le tas contient. Trop de shards, trop de champs, fielddata sur du texte, des agrégations surdimensionnées, des scripts lourds et des rôles mixtes sur le même nœud sont des causes plus courantes que "Elasticsearch a besoin de toute la RAM". Le travail de réglage du tas le plus fort se termine souvent par un mapping plus petit, moins de shards, de meilleures limites de requête, ou un niveau d'ingestion dédié.

Le réglage du tas n'est pas une cérémonie unique. Cela fait partie de la gestion de la capacité. Lorsque le volume de données augmente, les tableaux de bord changent, ou une nouvelle équipe commence à envoyer des documents plus larges, le profil mémoire change aussi. Gardez le dimensionnement du tas ennuyeux : mesurez, changez une chose, redémarrez en toute sécurité et vérifiez avec des données de charge de travail réelles.