- Choisir une clé primaire - Les schémas par défaut utilisent un
ORDER BYoptimisé pour des modèles d’accès spécifiques. Il est peu probable que vos modèles d’accès y correspondent. - Extraire la structure - Vous pouvez souhaiter extraire de nouvelles colonnes à partir de colonnes existantes, par exemple la colonne
Body. Cela peut se faire à l’aide de colonnes matérialisées (et de vues matérialisées dans les cas plus complexes). Cela nécessite des modifications du schéma. - Optimiser les Maps - Les schémas par défaut utilisent le type Map pour stocker les attributs. Ces colonnes permettent de stocker des métadonnées arbitraires. Bien qu’il s’agisse d’une capacité essentielle — les métadonnées des événements n’étant souvent pas définies à l’avance et ne pouvant donc pas être stockées autrement dans une base de données fortement typée comme ClickHouse — l’accès aux clés de map et à leurs valeurs est moins efficace que l’accès à une colonne classique. Pour y remédier, nous modifions le schéma afin que les clés de map les plus fréquemment consultées deviennent des colonnes de premier niveau ; voir “Extracting structure with SQL”. Cela nécessite une modification du schéma.
- Simplifier l’accès aux clés de map - L’accès aux clés dans les maps nécessite une syntaxe plus verbeuse. Vous pouvez atténuer cela avec des alias. Voir “Using Aliases” pour simplifier les requêtes.
- Index secondaires - Le schéma par défaut utilise des index secondaires pour accélérer l’accès aux Maps et les requêtes textuelles. Ils ne sont généralement pas nécessaires et consomment de l’espace disque supplémentaire. Ils peuvent être utilisés, mais doivent être testés pour vérifier qu’ils sont réellement utiles. Voir “Secondary / Data Skipping indices”.
- Utiliser des codecs - Vous pouvez souhaiter personnaliser les codecs des colonnes si vous comprenez les caractéristiques des données attendues et disposez d’éléments montrant que cela améliore la compression.
Extraction de structure avec SQL
- Extraire des colonnes à partir de blobs de chaînes. Les requêtes sur ces colonnes seront plus rapides que l’utilisation d’opérations sur les chaînes au moment de la requête.
- Extraire des clés de Map. Le schéma par défaut place les attributs arbitraires dans des colonnes de type Map. Ce type offre une capacité sans schéma, avec l’avantage que les utilisateurs n’ont pas besoin de prédéfinir les colonnes d’attributs lors de la définition des logs et des traces — ce qui est souvent impossible lors de la collecte de logs depuis Kubernetes si l’on veut s’assurer que les labels des pods sont conservés pour des recherches ultérieures. L’accès aux clés d’une Map et à leurs valeurs est plus lent que les requêtes sur des colonnes ClickHouse classiques. Il est donc souvent préférable d’extraire les clés des Map vers les colonnes de premier niveau de la table.
Body sous forme de String. De plus, il peut aussi être stocké dans la colonne LogAttributes sous la forme d’un Map(String, String) si l’utilisateur a activé le json_parser dans le collector.
LogAttributes est disponible, voici la requête permettant de compter les chemins d’URL du site qui reçoivent le plus de requêtes POST :
LogAttributes['request_path'], ainsi que de la fonction path pour supprimer les paramètres de requête de l’URL.
Si l’utilisateur n’a pas activé le parsing JSON dans le collector, LogAttributes sera vide, ce qui nous obligera à utiliser les fonctions JSON pour extraire les colonnes du Body de type String.
Privilégiez ClickHouse pour le parsingNous recommandons généralement aux utilisateurs d’effectuer le parsing JSON des logs structurés dans ClickHouse. Nous sommes convaincus que ClickHouse propose l’implémentation de parsing JSON la plus rapide. Cela dit, nous comprenons que vous puissiez vouloir envoyer les logs vers d’autres sources sans intégrer cette logique dans SQL.
extractAllGroupsVertical.
Pensez aux DictionariesLa requête ci-dessus შეიძლება être optimisée pour exploiter des Dictionaries d’expressions régulières. Voir Using Dictionaries pour plus de détails.
OTel ou ClickHouse pour le traitement ?Vous pouvez également effectuer le traitement à l’aide des processors et operators de l’OTel collector, comme décrit ici. Dans la plupart des cas, vous constaterez que ClickHouse est nettement plus économe en ressources et plus rapide que les processors du collector. Le principal inconvénient d’effectuer tout le traitement des événements en SQL est le couplage de votre solution à ClickHouse. Par exemple, vous pouvez souhaiter envoyer les logs traités vers d’autres destinations depuis l’OTel collector, par ex. S3.
Colonnes matérialisées
SurcoûtLes colonnes matérialisées entraînent un surcoût de stockage supplémentaire, car les valeurs sont extraites vers de nouvelles colonnes sur disque au moment de l’insertion.
LogAttributes par le collector :
Body de type String se trouve ici.
Nos trois colonnes matérialisées extraient la page demandée, le type de requête et le domaine du référent. Elles accèdent aux clés de la map et appliquent des fonctions à leurs valeurs. La requête suivante est nettement plus rapide :
Par défaut, les colonnes matérialisées ne sont pas renvoyées dans un
SELECT *. Cela permet de préserver l’invariant selon lequel le résultat d’un SELECT * peut toujours être réinséré dans la table à l’aide d’INSERT. Ce comportement peut être désactivé en définissant asterisk_include_materialized_columns=1 et peut être activé dans Grafana (voir Additional Settings -> Custom Settings dans la configuration de la source de données).Vues matérialisées
Mises à jour en temps réelLes vues matérialisées dans ClickHouse sont mises à jour en temps réel à mesure que les données arrivent dans la table sur laquelle elles reposent, et fonctionnent davantage comme des index continuellement mis à jour. À l’inverse, dans d’autres bases de données, les vues matérialisées sont généralement des instantanés statiques d’une requête qui doivent être actualisés (comme les vues matérialisées actualisables de ClickHouse).
SELECT est possible.
Gardez à l’esprit que la requête n’est qu’un trigger qui s’exécute sur les lignes insérées dans une table (la table source), les résultats étant envoyés vers une nouvelle table (la table cible).
Afin d’éviter de persister les données deux fois (dans les tables source et cible), nous pouvons remplacer le moteur de la table source par un Null table engine, tout en conservant le schéma d’origine. Nos OTel collectors continueront à envoyer les données vers cette table. Par exemple, pour les logs, la table otel_logs devient :
/dev/null. Cette table ne stocke aucune donnée, mais toutes les vues matérialisées attachées sont tout de même exécutées sur les lignes insérées avant que celles-ci ne soient ignorées.
Considérez la requête suivante. Elle transforme nos lignes dans un format que nous souhaitons conserver, en extrayant toutes les colonnes de LogAttributes (nous supposons que cela a été défini par le collector à l’aide de l’opérateur json_parser), en définissant SeverityText et SeverityNumber (sur la base de quelques conditions simples et de la définition de ces colonnes). Dans ce cas, nous sélectionnons également uniquement les colonnes dont nous savons qu’elles seront renseignées, en ignorant des colonnes telles que TraceId, SpanId et TraceFlags.
Body ci-dessus — au cas où des attributs supplémentaires seraient ajoutés ultérieurement sans être extraits par notre SQL. Cette colonne devrait bien se compresser dans ClickHouse et sera rarement consultée, sans impact sur les performances des requêtes. Enfin, nous réduisons le Timestamp en DateTime (pour économiser de l’espace — voir « Optimisation des types ») via un cast.
Expressions conditionnellesNotez l’utilisation des expressions conditionnelles ci-dessus pour extraire
SeverityText et SeverityNumber. Elles sont extrêmement utiles pour formuler des conditions complexes et vérifier si des valeurs sont définies dans des maps ; ici, nous supposons naïvement que toutes les clés existent dans LogAttributes. Nous recommandons aux utilisateurs de bien s’y familiariser : elles sont très utiles pour le parsing des logs, en complément des fonctions de gestion des valeurs NULL !Notez à quel point nous avons profondément modifié notre schéma. En pratique, vous aurez probablement aussi des colonnes de traces à conserver, ainsi que la colonne
ResourceAttributes (qui contient généralement des métadonnées Kubernetes). Grafana peut exploiter les colonnes de traces pour créer des liens entre les logs et les traces - voir “Utiliser Grafana”.otel_logs_mv, qui exécute l’instruction SELECT ci-dessus pour la table otel_logs et envoie les résultats vers otel_logs_v2.
otel_logs_v2 au format souhaité. Notez l’utilisation de fonctions typées d’extraction JSON.
Body à l’aide de fonctions JSON, est présentée ci-dessous :
Attention aux types
LogAttributes. ClickHouse convertit souvent de manière transparente la valeur extraite vers le type de la table cible, ce qui réduit la quantité de syntaxe nécessaire. Nous recommandons toutefois aux utilisateurs de toujours tester leurs vues en utilisant l’instruction SELECT de la vue avec une instruction INSERT INTO vers une table cible utilisant le même schéma. Cela devrait permettre de confirmer que les types sont correctement gérés. Portez une attention particulière aux cas suivants :
- Si une clé n’existe pas dans une map, une chaîne vide sera renvoyée. Pour les valeurs numériques, vous devrez la convertir en une valeur appropriée. Cela peut être fait avec des fonctions conditionnelles, par ex.
if(LogAttributes['status'] = ", 200, LogAttributes['status']), ou avec des fonctions de transtypage si des valeurs par défaut sont acceptables, par ex.toUInt8OrDefault(LogAttributes['status'] ) - Certains types ne sont pas toujours convertis ; par ex., les représentations textuelles de valeurs numériques ne seront pas converties en valeurs d’enum.
- Les fonctions d’extraction JSON renvoient les valeurs par défaut de leur type si aucune valeur n’est trouvée. Assurez-vous que ces valeurs sont pertinentes !
Évitez NullableÉvitez d’utiliser Nullable dans ClickHouse pour les données d’observabilité. Il est rarement nécessaire, pour les logs et les traces, de distinguer une valeur vide d’une valeur NULL. Cette fonctionnalité entraîne un surcoût de stockage et aura un impact négatif sur les performances des requêtes. Voir ici pour plus de détails.
Choisir une clé primaire (de tri)
- Sélectionnez des colonnes qui correspondent à vos filtres les plus courants et à vos habitudes d’accès. Si vous commencez généralement vos investigations d’observabilité en filtrant sur une colonne donnée, par exemple le nom du pod, cette colonne sera fréquemment utilisée dans les clauses
WHERE. Priorisez son inclusion dans votre clé plutôt que celle de colonnes moins souvent utilisées. - Privilégiez les colonnes qui permettent d’exclure une grande part du nombre total de lignes lorsqu’un filtre est appliqué, afin de réduire la quantité de données à lire. Les noms de service et les codes d’état sont souvent de bons candidats — dans le second cas, uniquement si vous filtrez sur des valeurs qui excluent la majorité des lignes. Par exemple, filtrer sur des codes 200 correspondra, dans la plupart des systèmes, à la majorité des lignes, contrairement aux erreurs 500, qui ne représenteront qu’un petit sous-ensemble.
- Privilégiez les colonnes susceptibles d’être fortement corrélées à d’autres colonnes de la table. Cela permet aussi de garantir que ces valeurs soient stockées de façon contiguë, ce qui améliore la compression.
- Les opérations
GROUP BYetORDER BYsur les colonnes de la clé de tri peuvent être plus économes en mémoire.
Une fois le sous-ensemble de colonnes de la clé de tri identifié, celles-ci doivent être déclarées dans un ordre précis. Cet ordre peut fortement influencer à la fois l’efficacité du filtrage sur les colonnes de clé secondaires dans les requêtes et le taux de compression des fichiers de données de la table. En règle générale, il est préférable d’ordonner les clés par cardinalité croissante. Il faut toutefois tenir compte du fait que le filtrage sur les colonnes situées plus loin dans la clé de tri sera moins efficace que sur celles placées plus tôt dans le tuple. Trouvez le bon équilibre entre ces comportements et tenez compte de vos habitudes d’accès. Surtout, testez différentes variantes. Pour mieux comprendre les clés de tri et savoir comment les optimiser, nous recommandons cet article.
La structure avant toutNous vous recommandons de définir vos clés de tri une fois vos logs structurés. N’utilisez ni les clés des maps d’attributs ni des expressions d’extraction JSON pour la clé de tri. Assurez-vous que vos clés de tri figurent comme colonnes de premier niveau dans votre table.
Utiliser les maps
map['key'] pour accéder aux valeurs des colonnes Map(String, String). Outre l’utilisation de la notation map pour accéder aux clés imbriquées, ClickHouse propose des fonctions map spécialisées pour filtrer ou sélectionner ces colonnes.
Par exemple, la requête suivante permet d’identifier toutes les clés uniques présentes dans la colonne LogAttributes à l’aide de la fonction mapKeys, suivie de la fonction groupArrayDistinctArray (un combinateur).
Évitez les pointsNous ne recommandons pas d’utiliser des points dans les noms de colonnes Map et pourrions abandonner cette possibilité à l’avenir. Utilisez plutôt un
_.Utilisation des alias
ALIAS, RemoteAddr, qui accède à la map LogAttributes. Nous pouvons désormais interroger via cette colonne les valeurs de LogAttributes['remote_addr'], ce qui simplifie notre requête, c.-à-d.
ALIAS via la commande ALTER TABLE est trivial. Ces colonnes sont immédiatement disponibles, par exemple
Les alias sont exclus par défautPar défaut,
SELECT * exclut les colonnes ALIAS. Ce comportement peut être désactivé en définissant asterisk_include_alias_columns=1.Optimisation des types
Utilisation des codecs
ZSTD est particulièrement bien adapté aux jeux de données de logs et de traces. Augmenter le niveau de compression par rapport à sa valeur par défaut de 1 peut améliorer la compression. Cela doit toutefois être testé, car des valeurs plus élevées entraînent une surcharge CPU plus importante au moment de l’insert. En pratique, nous constatons généralement peu de gain à augmenter cette valeur.
En outre, les timestamps, bien qu’ils bénéficient de l’encodage delta en termes de compression, peuvent dégrader les performances des query si cette colonne est utilisée dans la clé primaire/de tri. Nous recommandons aux utilisateurs d’évaluer le compromis entre compression et performances des query.
Utilisation des dictionnaires
Accélérer les jointuresLes utilisateurs qui souhaitent accélérer les jointures avec des dictionnaires trouveront plus de détails ici.
À l’insertion vs au moment de la requête
- À l’insertion - Cette approche est généralement adaptée si la valeur d’enrichissement ne change pas et provient d’une source externe pouvant servir à alimenter le dictionnaire. Dans ce cas, enrichir la ligne à l’insertion évite une recherche dans le dictionnaire au moment de la requête. Cela a toutefois un coût en termes de performances d’insertion, ainsi qu’un surcoût de stockage, car les valeurs enrichies seront stockées dans des colonnes.
- Au moment de la requête - Si les valeurs d’un dictionnaire changent fréquemment, les recherches au moment de la requête sont souvent plus adaptées. Cela évite d’avoir à mettre à jour les colonnes (et à réécrire les données) si les valeurs associées changent. Cette souplesse se paie toutefois par le coût d’une recherche au moment de la requête. Ce coût est généralement sensible lorsqu’une recherche est nécessaire pour un grand nombre de lignes, par exemple lorsqu’une recherche dans le dictionnaire est utilisée dans une clause de filtre. Pour l’enrichissement des résultats, c’est-à-dire dans le
SELECT, ce surcoût est généralement négligeable.
Utilisation des dictionnaires IP
ip_trie.
Nous utilisons le jeu de données DB-IP à l’échelle des villes, mis à disposition publiquement par DB-IP.com dans le cadre de la licence CC BY 4.0.
D’après le README, les données sont structurées comme suit :
URL() pour créer une table ClickHouse avec nos noms de champ et vérifier le nombre total de lignes :
ip_trie exige que les plages d’adresses IP soient exprimées au format CIDR, nous devons transformer ip_range_start et ip_range_end.
Le CIDR correspondant à chaque plage peut être calculé simplement avec la requête suivante :
Cette requête fait beaucoup de choses. Pour celles et ceux que cela intéresse, lisez cette excellente explication. Sinon, retenez simplement qu’elle calcule un CIDR pour une plage d’adresses IP.
ip_trie pour associer nos préfixes réseau (blocs CIDR) à des coordonnées et des codes pays. La requête suivante définit un dictionnaire utilisant ce layout et la table ci-dessus comme source.
Actualisation périodiqueLes dictionnaires ClickHouse sont actualisés périodiquement en fonction des données de la table sous-jacente et de la clause lifetime utilisée ci-dessus. Pour mettre à jour notre dictionnaire Geo IP afin qu’il reflète les dernières modifications du jeu de données DB-IP, il suffit de réinsérer les données de la table distante geoip_url dans notre table
geoip, avec les transformations appliquées.ip_trie (qui, commodément, porte lui aussi le nom ip_trie), nous pouvons l’utiliser pour la géolocalisation d’adresses IP. Pour cela, utilisez la fonction dictGet() comme suit :
RemoteAddress extraite.
Mise à jour périodiqueLes utilisateurs souhaiteront probablement que le dictionnaire d’enrichissement d’IP soit mis à jour périodiquement à partir des nouvelles données. Cela peut se faire à l’aide de la clause
LIFETIME du dictionnaire, qui entraînera son rechargement périodique depuis la table sous-jacente. Pour mettre à jour la table sous-jacente, voir “Vues matérialisées actualisables”.Utilisation des dictionnaires Regex (analyse des user-agents)
Dans les exemples ci-dessous, nous utilisons des snapshots des expressions régulières uap-core les plus récentes pour l’analyse des user-agents, datant de juin 2024. Le fichier le plus récent, mis à jour occasionnellement, est disponible ici. Vous pouvez suivre les étapes ici pour charger le fichier CSV utilisé ci-dessous.
otel_logs_v2 :
Tuples pour les structures complexesNotez l’utilisation de Tuples pour ces colonnes de user agent. Les Tuples sont recommandés pour les structures complexes dont la hiérarchie est connue à l’avance. Les sous-colonnes offrent les mêmes performances que les colonnes classiques (contrairement aux clés de Map) tout en permettant des types hétérogènes.
Pour aller plus loin
- Sujets avancés des dictionnaires
- “Utiliser les dictionnaires pour accélérer les requêtes”
- Dictionnaires
Accélérer les requêtes
Utilisation des vues matérialisées (incrémental) pour les agrégations
Cette requête serait 10x plus rapide si nous utilisions la table
otel_logs_v2, issue de notre vue matérialisée précédente, qui extrait la clé size de la map LogAttributes. Nous utilisons ici les données brutes uniquement à des fins d’illustration et recommandons d’utiliser la vue précédente s’il s’agit d’une requête courante.bytes_per_hour soit vide et n’ait encore reçu aucune donnée. Notre vue matérialisée exécute le SELECT ci-dessus sur les données insérées dans otel_logs (cette opération s’effectue sur des blocs d’une taille configurée), puis envoie les résultats vers bytes_per_hour. La syntaxe est présentée ci-dessous :
TO est essentielle ici, car elle indique où les résultats seront envoyés, c’est-à-dire dans bytes_per_hour.
Si nous redémarrons notre OTel Collector et renvoyons les logs, la table bytes_per_hour sera alimentée de manière incrémentale avec le résultat de la requête ci-dessus. Une fois l’opération terminée, nous pouvons vérifier la taille de bytes_per_hour : nous devrions avoir 1 ligne par heure :
otel_logs) à 113 en stockant le résultat de notre requête. L’idée clé est que, si de nouveaux logs sont insérés dans la table otel_logs, de nouvelles valeurs seront envoyées à bytes_per_hour pour l’heure correspondante, où elles seront automatiquement fusionnées de manière asynchrone en arrière-plan. En ne conservant qu’une seule ligne par heure, bytes_per_hour restera donc toujours à la fois compacte et à jour.
Comme la fusion des lignes est asynchrone, il peut y avoir plus d’une ligne par heure au moment où un utilisateur exécute une requête. Pour garantir que toutes les lignes en attente soient fusionnées au moment de la requête, nous avons deux options :
- Utiliser le
modificateur FINALsur le nom de la table (ce que nous avons fait pour la requête de comptage ci-dessus). - Agréger selon la clé d’ordre utilisée dans notre table finale, c.-à-d. Timestamp, et sommer les métriques.
Ces gains peuvent être encore plus importants sur des jeux de données plus volumineux et avec des requêtes plus complexes. Voir ici pour des exemples.
Un exemple plus complexe
UniqueUsers avec le type AggregateFunction, en précisant la fonction à l’origine des états partiels (uniq) ainsi que le type de la colonne source (IPv4). Comme avec le SummingMergeTree, les lignes ayant la même valeur de clé ORDER BY seront fusionnées (Hour dans l’exemple ci-dessus).
La vue matérialisée associée utilise la requête précédente :
State à la fin de nos fonctions d’agrégation. Cela garantit que l’état d’agrégation de la fonction est renvoyé à la place du résultat final. Cet état contiendra des informations supplémentaires lui permettant de fusionner avec d’autres états.
Une fois les données rechargées, à la suite d’un redémarrage du collector, nous pouvons confirmer que 113 lignes sont présentes dans la table unique_visitors_per_hour.
GROUP BY plutôt que FINAL.
Utilisation des vues matérialisées (incrémentales) pour des recherches rapides
ServiceName, SpanName et Timestamp. En tracing, les utilisateurs doivent aussi pouvoir effectuer des recherches à partir d’un TraceId spécifique et récupérer les spans associés à la trace correspondante. Bien que cet élément figure dans la clé de tri, sa position en fin de clé signifie que le filtrage ne sera pas aussi efficace et qu’il faudra probablement analyser des volumes importants de données pour récupérer une seule trace.
L’OTel collector installe également une vue matérialisée et la table associée pour répondre à ce problème. La table et la vue sont présentées ci-dessous :
otel_traces_trace_id_ts contient l’horodatage minimal et maximal de la trace. Cette table, triée par TraceId, permet de récupérer efficacement ces horodatages. Ces plages d’horodatage peuvent ensuite être utilisées pour interroger la table principale otel_traces. Plus précisément, lorsque Grafana récupère une trace à partir de son identifiant, il utilise la requête suivante :
ae9226c78d1d360601e6383928e4d22d, avant de les utiliser pour filtrer la table principale otel_traces afin de récupérer les spans associés.
Cette même approche peut être appliquée à des schémas d’accès similaires. Nous présentons un exemple semblable dans la section Modélisation des données ici.
Utilisation des projections
ORDER BY pour une table.
Dans les sections précédentes, nous avons vu comment les vues matérialisées peuvent être utilisées dans ClickHouse pour précalculer des agrégations, transformer des lignes et optimiser les requêtes d’observability selon différents schémas d’accès.
Nous avons présenté un exemple dans lequel la vue matérialisée envoie des lignes vers une table cible avec une clé de tri différente de celle de la table d’origine qui reçoit les insertions, afin d’optimiser les recherches par ID de trace.
Les projections peuvent être utilisées pour résoudre le même problème, en permettant à l’utilisateur d’optimiser les requêtes sur une colonne qui ne fait pas partie de la clé primaire.
En théorie, cette possibilité peut être utilisée pour fournir plusieurs clés de tri pour une table, avec un inconvénient majeur : la duplication des données. Plus précisément, les données devront être écrites dans l’ordre de la clé primaire principale, en plus de l’ordre spécifié pour chaque projection. Cela ralentira les insertions et consommera davantage d’espace disque.
Projections vs vues matérialiséesLes projections offrent bon nombre des mêmes possibilités que les vues matérialisées, mais doivent être utilisées avec parcimonie, ces dernières étant souvent à privilégier. Vous devez en comprendre les inconvénients et savoir dans quels cas elles sont appropriées. Par exemple, même si les projections peuvent être utilisées pour précalculer des agrégations, nous recommandons d’utiliser des vues matérialisées pour cela.
otel_logs_v2 sur les codes d’erreur 500. Il s’agit probablement d’un schéma d’accès courant pour la journalisation, les utilisateurs souhaitant filtrer par codes d’erreur :
Utilisez Null pour mesurer les performancesNous n’affichons pas les résultats ici en utilisant
FORMAT Null. Cela force la lecture de tous les résultats sans les renvoyer, ce qui évite qu’une requête se termine prématurément à cause d’une LIMIT. Cela sert uniquement à montrer le temps nécessaire pour parcourir l’ensemble des 10m lignes.(ServiceName, Timestamp). Bien que nous puissions ajouter Status à la fin de la clé de tri, ce qui améliorerait les performances de la requête ci-dessus, nous pouvons aussi ajouter une projection.
ALTER, sa création est asynchrone lorsque la commande MATERIALIZE PROJECTION est exécutée. Vous pouvez suivre l’avancement de cette opération avec la query suivante, en attendant que is_done=1.
SELECT * ici, toutes les colonnes seraient stockées. Bien que cela permette à davantage de requêtes (utilisant n’importe quel sous-ensemble de colonnes) de tirer parti de la projection, cela entraînerait un stockage supplémentaire. Pour mesurer l’espace disque et la compression, consultez “Mesurer la taille et la compression d’une table”.
Index secondaires / data skipping indices
Index de texte pour la recherche en texte intégral
tokenizer dans sa définition. Il est également possible de spécifier une fonction de prétraitement afin de transformer la chaîne d’entrée avant la tokenisation.
Les fonctions recommandées pour rechercher dans l’index sont : hasAnyTokens et hasAllTokens.
Certaines fonctions traditionnelles de recherche dans les chaînes sont également automatiquement optimisées lorsqu’un index de texte est présent.
Consultez la documentation pour plus de détails ainsi que la liste des fonctions prises en charge ici et ici.
Dans les exemples ci-dessous, nous utilisons un jeu de données de logs structurés.
hasAnyTokens sans index de texte, mais la requête devra effectuer un parcours complet, donc lent, de la colonne Body :
Ajout d’un index de texte
ALTER TABLE :
Utiliser un préprocesseur
msg, id, ctx, attr, etc.).
Supposons que nous souhaitions rechercher uniquement dans le champ msg.
Au lieu d’indexer l’ensemble de la chaîne JSON, nous pouvons définir un préprocesseur afin d’extraire uniquement la valeur msg avant la tokenisation.
Par exemple :
- réduit la quantité de texte tokenisé et indexé,
- diminue la taille de l’index,
- réduit la probabilité de faux positifs,
- et améliore les performances des requêtes.