Résoudre les problèmes de lenteur des requêtes Elasticsearch
Elasticsearch est une application très flexible, dotée de nombreuses fonctionnalités, qui propose un éventail de méthodes pour interroger vos données. Malheureusement, il arrive parfois que la vitesse des requêtes ne soit pas à la hauteur de vos espérances. Ce problème vous dit-il quelque chose ? En avez-vous déjà fait l’expérience ? Avec un système distribué comme Elasticsearch, de nombreux facteurs ont une influence sur les performances des requêtes, y compris des facteurs externes comme les paramètres d’équilibrage des charges, la latence du réseau (bande passante, cartes/pilotes réseau), etc.
Dans cet article de blog, nous allons voir ce qui peut être à l’origine de la lenteur des requêtes et comment repérer les requêtes lentes dans Elasticsearch. Nous étudierons plusieurs méthodes de résolution, qui impliquent d’avoir une bonne connaissance du fonctionnement d’Elasticsearch.
Causes fréquentes de lenteur des requêtes Elasticsearch
Avant de nous pencher sur des cas plus complexes, commençons par quelques-unes des causes les plus fréquentes de lenteur des requêtes, et leurs solutions.
Symptôme : Consommation élevée de ressources malgré un état inactif
Chaque partition consomme des ressources (processeur/mémoire). Même s’il n’y a aucune requête d’indexation ou de recherche, la présence de partitions entraîne la surconsommation de ressources de cluster.
Problème
Le cluster compte un si grand nombre de partitions que chaque requête semble prendre du temps à s’exécuter. Une bonne règle générale est de s'assurer que le nombre de partitions non gelées par nœud reste en dessous de 20 par Go de segment de mémoire configuré.
Solution
Réduire le nombre de partitions, geler les index et/ou ajouter d’autres nœuds pour gérer la charge. Pour contrôler efficacement le nombre de partitions, une solution consiste à mettre en place une architecture hot/warm (qui fonctionne très bien pour les index temporels) et d’utiliser la fonctionnalité de substitution/réduction d’Elasticsearch. Avant de réaliser un déploiement, planifiez correctement la capacité afin de déterminer le nombre optimal de partitions pour chaque cas d'utilisation de recherche.
Symptôme : Grand nombre de rejets du pool de threads
Le pool de threads de recherche affiche un nombre croissant de "rejets" qui se cumulent depuis le dernier redémarrage du cluster.
GET /_cat/thread_pool/search?v&h=node_name,name,active,rejected,completed
La réponse se présente comme suit :
node_name name active rejected completed instance-0000000001 search 0 10 0 instance-0000000002 search 0 20 0 instance-0000000003 search 0 30 0
Problème
Les requêtes ciblent un trop grand nombre de partitions et le nombre de cœurs n’est pas suffisant dans le cluster. De ce fait, des tâches sont mises en file d’attente dans le pool de threads de recherche, ce qui entraîne des rejets de recherche. Autre cause fréquente : les E/S de disque sont trop lentes. Les recherches s’accumulent donc dans les files d’attente, ou, dans certains cas, le processeur est complètement saturé.
Solution
Optez pour un modèle 1 partition primaire : 1 réplique lorsque vous créez des index. Appuyez-vous sur un modèle d’index pour appliquer cette configuration lors de l’indexation. (Le modèle 1P:1R est utilisé par défaut à partir d’Elasticsearch 7.0). À partir de la version 5.1, Elasticsearch prend en charge l’annulation des tâches de recherche, ce qui peut être utile si des requêtes lentes sont détectées dans l’API de gestion des tâches. Pour améliorer les E/S de disque, consultez nos recommandations en matière de stockage et utilisez le matériel recommandé pour bénéficier de performances optimales.
Symptôme : Grande consommation de processeur et latence élevée au niveau de l’indexation
La mise en corrélation des indicateurs montre une consommation élevée du processeur et une latence forte au niveau de l’indexation en cas de surcharge du cluster.
Problème
Le cluster réalise une indexation massive, ce qui affecte les performances de recherche.
Solution
Pour améliorer les performances d’indexation, une solution consiste à augmenter le paramètre index.refresh_ interval (c’est-à-dire la durée entre le moment où un document est indexé et le moment où il est visible) à 30 secondes par exemple. L’utilisation peut varier. Il est donc nécessaire de faire des tests. Le but : faire en sorte que les partitions n’aient pas un rythme trop soutenu qui impose de créer un segment par seconde (paramètre par défaut).
Pour les cas d'utilisation impliquant une indexation massive, consultez nos recommandations en matière de réglage de l’index pour optimiser les performances de l’index et de la recherche.
Symptôme : Augmentation de la latence suite à l’augmentation des répliques
On observe une certaine latence au niveau des requêtes après que des répliques de partition ont été ajoutées (par exemple, si l’on passe d’une partition à deux). Si le volume de données augmente, alors les données mises en cache sont écartées plus rapidement, ce qui conduit à une augmentation du nombre d’erreurs sur la page du système d'exploitation.
Problème
Le cache du système de fichiers ne dispose pas d’une mémoire suffisante pour mettre en cache les parties les plus interrogées de l’index. Le cache de requête d’Elasticsearch applique une politique d’expulsion selon l’ancienneté : lorsque le cache est plein, les données les plus anciennes sont écartées pour faire de la place aux nouvelles données.
Solution
Réservez au moins 50 % de la RAM physique pour le cache du système de fichiers. Plus vous disposez de mémoire, plus vous pouvez mettre de requêtes en cache, en particulier si le cluster a des problèmes d’E/S. Si l’on part du principe que le bloc de mémoire est correctement configuré, la RAM physique restante pouvant être utilisée pour le cache du système de fichiers aide à améliorer les performances de recherche.
Supposons que vous disposiez d’un serveur RAM de 128 Go, dont 30 Go réservés pour le bloc de mémoire et le reste pour le cache du système de fichiers (parfois appelé cache du système d’exploitation). Le système d'exploitation peut ainsi mettre en cache 4 Ko de blocs de données récemment consultés. Ainsi, si vous lisez les mêmes fichiers encore et encore, vous n’aurez pas besoin d’accéder au disque la plupart du temps ; la requête sera effectuée directement à partir de la mémoire.
Outre le cache du système de fichiers, Elasticsearch se sert du cache de recherche et du cache de requête pour accélérer les recherches. Ces caches peuvent être optimisés à l’aide d’une préférence de requête de recherche pour acheminer certaines requêtes vers le même ensemble de partitions à chaque fois, au lieu d’alterner entre les différentes copies disponibles. Ainsi, le potentiel de ces caches sera exploité de façon appropriée.
Symptôme : Consommation élevée lors du partage de ressources
Le système d'exploitation affiche une utilisation élevée du processeur et des E/S de disque. On constate des gains de performances après l’arrêt d’applications tierces.
Problème
On observe des conflits de ressources (processeur et/ou E/S de disque) entre d’autres processus (p. ex. Logstash) et Elasticsearch.
Solution
Évitez d’exécuter Elasticsearch avec d’autres applications gourmandes en ressources sur un matériel partagé.
Symptôme : Consommation de mémoire élevée lors de l’agrégation de champs uniques
Performances faibles lors de l’interrogation de champs agrégés contenant des valeurs uniques (p. ex. ID, noms d'utilisateur, adresses e-mail, etc.). Lors de l’analyse du dump mémoire, vous devriez voir des objets Java avec des termes tels que "search", "buckets", "aggregation", etc., utilisant une grande part du bloc de mémoire.
Problème
Les agrégations s’exécutent sur des champs à forte cardinalité nécessitant un grand volume de ressources pour récupérer de nombreux buckets. Il peut également y avoir des agrégations imbriquées impliquant des champs imbriqués et/ou des champs de jointure.
Solution
Pour améliorer les performances des agrégations de termes à forte cardinalité, lisez cet article de blog rédigé par mon collègue de l’équipe de conseil : https://www.elastic.co/fr/blog/improving-the-performance-of-high-cardinality-terms-aggregations-in-elasticsearch (en anglais).
Pour optimiser davantage les performances, découvrez nos recommandations sur les champs imbriqués et les champs de jointure.
Requêtes lentes occasionnelles
De façon générale, les requêtes qui sont lentes de façon occasionnelle ou intermittente peuvent être améliorées grâce à nos recommandations sur l’optimisation de l’index et l’optimisation de la recherche. Ces requêtes lentes occasionnelles devraient être mises en corrélation étroite avec un ou plusieurs indicateurs de monitoring :
- Charge du processeur
- Débit de l’indexation
- Débit de la recherche
- Activité de récupération de mémoire
- Taille de la file d’attente dans le pool de threads de recherche
Elasticsearch dispose d’une autre fonctionnalité utile appelée Adaptive Replica Selection (ARS) (sélection de réplique adaptative) qui permet au nœud coordinateur de connaître la charge s’exécutant sur les nœuds de données et de choisir la copie de partition la plus adaptée pour effectuer une recherche. Résultat : les performances de recherche sont optimisées et la latence est réduite. Cette fonctionnalité peut être d’une grande aide lors des ralentissements occasionnels, car elle répartit la charge de façon égale lors de la requête. À partir de la version 7.0 d’Elasticsearch, l’ARS est activée par défaut.
Requêtes lentes permanentes
Dans le cas d’une requête lente permanente, ce que nous pouvons essayer de faire, c’est de supprimer des fonctionnalités de la requête l’une après l’autre, puis de vérifier si la requête est toujours lente. En reproduisant le problème de performance à partir de la requête la plus simple possible, nous sommes en mesure d’isoler et d’identifier le problème plus facilement :
- La requête est-elle toujours lente sans la mise en surbrillance ?
- La requête est-elle toujours lente sans les agrégations ?
- Est-elle toujours lente si la
taille
est définie sur 0 ? (lorsque lataille
est définie sur 0, Elasticsearch met en cache les résultats des requêtes de recherche pour accélérer la recherche)
Est-il possible d’y appliquer des recommandations en matière d’optimisation de la recherche ?
Pour résoudre les problèmes, voici quelques actions utiles à exécuter :
- Obtenir une réponse à la requête avec le profil activé.
- Recueillir les résultats des threads hot des nœuds avec la requête qui s’exécute dans une boucle while(true). Cela permettra de comprendre l’utilisation du processeur.
- Établir un profil de la requête à l’aide de cette version conviviale de l’API de profil.
Si une requête provient d’une visualisation Kibana, utilisez le visualization spy panel (panneau des coulisses de la visualisation) (version 6.3 de Kibana et précédentes) ou le dashboard inspect panel (panneau d’inspection du tableau de bord) (à partir de la version 6.4 de Kibana) pour afficher et exporter la requête de recherche réelle et l’importer dans l’API de profil pour effectuer une analyse plus poussée.
Détection de requêtes lentes ou lourdes
Il est parfois difficile de détecter les requêtes lentes ou lourdes (c.-à-d. nécessitant beaucoup de ressources), étant donné que plusieurs requêtes ou threads sont traités simultanément au sein d’une application distribuée comme Elasticsearch. Les choses se compliquent davantage lorsqu’aucun contrôle n’est exercé sur les utilisateurs qui exécutent des requêtes lourdes ralentissant les performances du cluster (p. ex. cycles longs de récupération de mémoire), ou pire, provoquant une saturation de la mémoire.
À partir de la version 7.0 d’Elasticsearch, une nouvelle stratégie de disjoncteur fait son apparition. Celle-ci mesure l’utilisation réelle de la mémoire lorsqu’une partie du bloc de mémoire est réservée. Cette nouvelle stratégie améliore la résilience des nœuds par rapport aux requêtes lourdes qui surchargent le cluster. Elle est activée par défaut et peut être contrôlée avec le nouveau paramètre de cluster indices.breaker.total.use_real_memory.
Il s’agit là des cas les plus courants. Pour les cas non abordés dans ces solutions, il peut être utile de recueillir le dump mémoire après un crash en raison de la saturation de la mémoire ou le dump mémoire d’une JVM en cours d’exécution pour mieux comprendre la cause profonde.
Elasticsearch dispose aussi d’un autre atout dans sa manche : la limite non stricte du nombre maximal de buckets. Il s’agit d’un paramètre de protection visant à préserver le cluster des situations de saturation de la mémoire. Ce paramètre interrompt l’exécution de la requête de recherche si le nombre maximal de buckets est atteint (par défaut, 10 000 dans la version 7.0). Cela peut être le cas lors de l’exécution de plusieurs couches d’agrégations, par exemple.
Pour identifier les requêtes potentiellement lourdes, nous pouvons définir un paramètre de disjoncteur (indices.breaker.request.limit), en commençant par déterminer un seuil faible pour isoler les requêtes, puis en augmentant ensuite ce seuil de façon graduelle pour affiner notre recherche.
Slowlogs
Pour identifier les requêtes lentes, vous pouvez également activer les slowlogs dans Elasticsearch. Un slowlog intervient spécifiquement au niveau de la partition, c’est-à-dire uniquement au niveau des nœuds de données. Les nœuds coordinateurs/clients sont exclus car ils ne détiennent aucune donnée (index/partitions).
Les slowlogs permettent de répondre à des questions, telles que :
- Combien de temps la requête a-t-elle mis ?
- Quel était le contenu du corps de la requête de recherche ?
Exemple de sortie de slowlog :
[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[]],
Vue détaillée du message de slowlog :
Élément | Description |
[2019-02-11T16:47:39,882] | Date de la requête |
[TRACE] | Niveau de log |
[index.search.slowlog.query] | Phase de requête du slowlog de recherche |
[2g1yKIZ] | Nom du nœud |
[logstash-20190211] | Nom de l’index |
[4] | Numéro de partition sur la requête exécutée |
took[10.4s] | Durée de traitement sur la partition [4] Remarque : lorsque nous étudions les slowlogs, il ne faut pas ajouter les durées des différentes partitions, car ces partitions peuvent s’exécuter en parallèle. |
took_millis[10459] | Durée en millisecondes |
total_hits[16160] | Nombre total de résultats |
search_type[QUERY_THEN_FETCH] | Type de recherche |
total_shards[10] | Nombre total de partitions de l’index |
source[] | Corps de la requête exécutée |
Logs d’audit
Les clients ayant souscrit un abonnement Gold ou Platinum, comprenant des fonctionnalités de sécurité Elastic, peuvent activer les logs d’audit pour obtenir plus de détails sur la requête. (Les utilisateurs peuvent commencer avec une version d’essai de 30 jours pour tester les fonctionnalités de sécurité Elastic.) Le logging d'audit vous aide à répondre à des questions telles que :
- Quand la requête a-t-elle eu lieu ?
- Qui en est à l’origine ?
- Quel était le contenu de la requête ?
Il est nécessaire de régler les paramètres d’audit, car ceux définis par défaut sont plutôt larges :
- Pour activer les logs d’audit de sécurité : définissez
xpack.security.audit.enabled: true
dans elasticsearch.yml. - Pour activer le logging ou l’indexation dans les résultats des audits de sécurité : définissez
xpack.security.audit.outputs:[logfile, index]
dans elasticsearch.yml.
Remarques :- Le paramètre xpack.security.audit.outputs concerne uniquement les versions 6.0 à 6.2 et les versions 5.x. La version 7.0 n’accepte plus ce paramètre. Elle utilise par défaut la sortie json (<nom du cluster>_audit.json) lorsque xpack.security.audit.enabled est défini sur true.
- Dans le cadre de la résolution, nous vous recommandons d’opter pour les logs plutôt que l’index, car la verbosité du logging d'audit peut exercer une contrainte non voulue sur les performances du cluster et entraîner une extension de l’index de sécurité au-delà de la taille escomptée.
- Le mode d’audit peut être en effet très prolixe. Aussi, pensez à le désactiver une fois la résolution terminée.
- Inclure l’accès authentication_success dans votre liste d’évènements : définissez
xpack.security.audit.logfile.events.include: authentication_success
dans elasticsearch.yml.
Remarques :- Ce paramètre ne fait pas partie des événements par défaut. Si vous le définissez, il écrasera ceux proposés par défaut.
- Si vous devez ajouter (non pas remplacer) un événement de plus, écrivez d’abord la liste des événements par défaut existants, puis ajoutez le paramètre ci-dessus après la dernière entrée.
- Générez la sortie du corps de la requête dans les événements d’audit : définissez
xpack.security.audit.logfile.events.emit_request_body: true
dans elasticsearch.yml.
Avec ces paramètres, vous pouvez monitorer la requête utilisateur comme suit.
- Utilisateur : louisong
- Heure de la requête : 2019-05-01T19:26:53,554 (UTC)
- Point de terminaison de la requête : _msearch (signifie généralement qu’il s’agit d’une requête provenant d’une visualisation ou d’un tableau de bord Kibana)
- Corps de la requête : à partir de
"request.body":
dans le log ci-dessous :{"@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"}
Conclusion
Dans cet article, nous avons abordé les causes fréquentes à l’origine de la lenteur des requêtes et les solutions pour y remédier. Nous avons également vu différentes méthodes pour identifier les requêtes lentes occasionnelles et permanentes. Les requêtes lentes sont généralement un symptôme d’un problème de performance plus vaste au niveau du cluster, qu’il est nécessaire de résoudre.
Chez Elasticsearch, nous cherchons constamment à améliorer les temps de requête et nous mettons tout en œuvre pour accélérer les performances de recherche avec nos versions ultérieures. J’espère que cet article vous a donné des pistes pour gérer les requêtes lentes. N’hésitez pas à publier vos questions sur notre forum de discussion Elasticsearch si vous souhaitez approfondir le sujet. Bonne recherche !