- Les tables changent rarement ou lorsque des actualisations par lots sont acceptables.
- Les relations ne sont pas de type plusieurs-à-plusieurs ou n’ont pas une cardinalité trop élevée.
- Seul un sous-ensemble limité des colonnes sera interrogé, c.-à-d. que certaines colonnes peuvent être exclues de la dénormalisation.
- Vous pouvez déplacer le traitement hors de ClickHouse vers des systèmes en amont comme Flink, où l’enrichissement ou l’aplatissement en temps réel peut être géré.
Lorsque des JOIN sont nécessaires
- Évitez les produits cartésiens : si une valeur du côté gauche correspond à plusieurs valeurs du côté droit, le JOIN renverra plusieurs lignes — c’est ce qu’on appelle un produit cartésien. Si votre cas d’usage ne nécessite pas toutes les correspondances du côté droit, mais seulement une correspondance quelconque, vous pouvez utiliser des JOIN
ANY(par ex.LEFT ANY JOIN). Ils sont plus rapides et consomment moins de mémoire que les JOIN classiques. - Réduisez la taille des tables jointes : le temps d’exécution et la consommation mémoire des JOIN augmentent proportionnellement à la taille des tables de gauche et de droite. Pour réduire la quantité de données traitées par le JOIN, ajoutez des conditions de filtre supplémentaires dans les clauses
WHEREouJOIN ONde la requête. ClickHouse pousse les conditions de filtre aussi bas que possible dans le plan de requête, généralement avant les JOIN. Si les filtres ne sont pas automatiquement poussés vers le bas (pour une raison quelconque), réécrivez l’un des côtés du JOIN sous forme de sous-requête pour forcer le pushdown. - Utilisez des direct JOIN via des dictionnaires lorsque c’est approprié : les JOIN standard dans ClickHouse s’exécutent en deux phases : une phase de build, qui parcourt le côté droit pour construire une table de hachage, suivie d’une phase de probe, qui parcourt le côté gauche pour trouver les correspondances via des recherches dans la table de hachage. Si le côté droit est un dictionnaire ou un autre moteur de table présentant des caractéristiques clé-valeur (par ex. EmbeddedRocksDB ou le moteur de table Join), alors ClickHouse peut utiliser l’algorithme de jointure « direct », ce qui élimine effectivement la nécessité de construire une table de hachage et accélère le traitement des requêtes. Cela fonctionne pour les JOIN
INNERetLEFT OUTERet constitue l’option privilégiée pour les workloads analytiques en temps réel. - Exploitez le tri des tables pour les JOIN : dans ClickHouse, chaque table est triée selon ses colonnes de clé primaire. Il est possible d’exploiter ce tri en utilisant ce qu’on appelle des algorithmes de sort-merge JOIN comme
full_sorting_mergeetpartial_merge. Contrairement aux algorithmes de JOIN standard basés sur des tables de hachage (voir ci-dessous :parallel_hash,hash,grace_hash), les algorithmes de sort-merge JOIN trient d’abord les deux tables, puis les fusionnent. Si la requête joint les deux tables sur leurs colonnes de clé primaire respectives, sort-merge dispose d’une optimisation qui omet l’étape de tri, ce qui permet d’économiser du temps de traitement et de réduire le surcoût. - Évitez les JOIN avec spill sur disque : les états intermédiaires des JOIN (par ex. les tables de hachage) peuvent devenir si volumineux qu’ils ne tiennent plus en mémoire principale. Dans ce cas, ClickHouse renvoie par défaut une erreur de mémoire insuffisante. Certains algorithmes de JOIN (voir ci-dessous), par exemple
grace_hash,partial_mergeetfull_sorting_merge, peuvent écrire les états intermédiaires sur disque et poursuivre l’exécution de la requête. Ces algorithmes de JOIN doivent néanmoins être utilisés avec prudence, car les accès disque peuvent ralentir considérablement le traitement des JOIN. Nous recommandons plutôt d’optimiser la requête JOIN par d’autres moyens afin de réduire la taille des états intermédiaires. - Utilisez des valeurs par défaut comme marqueurs d’absence de correspondance dans les JOIN externes : les outer joins gauche/droite/complets incluent toutes les valeurs de la table gauche/droite/des deux tables. Si aucune correspondance n’est trouvée dans l’autre table pour une valeur donnée, ClickHouse remplace la ligne correspondante par un marqueur spécial. La norme SQL impose aux bases de données d’utiliser NULL comme marqueur. Dans ClickHouse, cela nécessite d’envelopper la colonne de résultat dans Nullable, ce qui entraîne un surcoût en mémoire et en performances. Vous pouvez aussi configurer le paramètre
join_use_nulls = 0et utiliser comme marqueur la valeur par défaut du type de données de la colonne de résultat.
Utilisez les dictionnaires avec précautionLorsque vous utilisez des dictionnaires pour des JOIN dans ClickHouse, il est important de comprendre que, par conception, les dictionnaires n’acceptent pas les clés en double. Lors du chargement des données, les clés en double sont dédupliquées silencieusement : seule la dernière valeur chargée pour une clé donnée est conservée. Ce comportement rend les dictionnaires idéaux pour les relations un-à-un ou plusieurs-à-un, lorsque seule la valeur la plus récente ou la valeur de référence est nécessaire. En revanche, utiliser un dictionnaire pour une relation un-à-plusieurs ou plusieurs-à-plusieurs (par exemple, joindre des rôles à des acteurs lorsqu’un acteur peut avoir plusieurs rôles) entraînera une perte silencieuse de données, car toutes les lignes correspondantes sauf une seront écartées. Par conséquent, les dictionnaires ne conviennent pas aux scénarios qui exigent une intégrité relationnelle complète lorsqu’il existe plusieurs correspondances. Pour en savoir plus sur les cas où les dictionnaires sont utiles (et ceux où ils ne le sont pas), consultez Bonnes pratiques pour les dictionnaires.
Choisir le bon algorithme de JOIN
- Parallel Hash JOIN (par défaut) : rapide pour les tables de droite de petite à moyenne taille qui tiennent en mémoire.
- Direct JOIN : idéal avec des dictionnaires (ou d’autres moteurs de table présentant des caractéristiques clé-valeur) avec
INNERouLEFT ANY JOIN— c’est la méthode la plus rapide pour les lookups ponctuels, car elle évite de devoir construire une table de hachage. - Full Sorting Merge JOIN : efficace lorsque les deux tables sont triées sur la clé de jointure.
- Partial Merge JOIN : minimise la consommation mémoire, mais est plus lent — convient le mieux pour joindre de grandes tables lorsque la mémoire est limitée.
- Grace Hash JOIN : flexible et ajustable en mémoire, bien adapté aux grands jeux de données avec des caractéristiques de performance modulables.
La prise en charge des types de JOIN varie selon l’algorithme. La liste complète des types de jointure pris en charge par chaque algorithme est disponible ici.
join_algorithm = 'auto' (par défaut), ou le contrôler explicitement selon votre charge de travail. Si vous devez sélectionner un algorithme de JOIN pour optimiser les performances ou réduire le surcoût mémoire, nous vous recommandons ce guide.
Pour des performances optimales :
- Réduisez autant que possible le nombre de JOIN dans les charges de travail exigeant de hautes performances.
- Évitez de dépasser 3 à 4 jointures par requête.
- Comparez les différents algorithmes sur des données réelles — les performances varient selon la distribution des clés de jointure et le volume des données.