Les enseignements de ce guide peuvent être appliqués à d’autres implémentations de stockage objet disposant de leurs propres fonctions de table dédiées, telles que GCS et Azure Blob storage.
Mécanismes d’insertion (nœud unique)
Taille du bloc d’insertion
INSERT INTO SELECT, ClickHouse reçoit une portion de données et ① forme, à partir des données reçues, au moins un bloc d’insertion en mémoire (par clé de partitionnement). Les données du bloc sont triées, puis des optimisations propres au moteur de table sont appliquées. Les données sont ensuite compressées puis ② écrites dans le stockage de la base de données sous la forme d’une nouvelle part de données.
La taille du bloc d’insertion a un impact à la fois sur l’activité d’E/S des fichiers sur disque et sur l’utilisation de la mémoire d’un serveur ClickHouse. Des blocs d’insertion plus grands consomment plus de mémoire, mais génèrent des parts initiales plus volumineuses et moins nombreuses. Moins ClickHouse doit créer de parts pour charger un grand volume de données, moins il faut d’E/S de fichiers sur disque et de fusions automatiques en arrière-plan.
Lorsqu’une requête INSERT INTO SELECT est utilisée avec un moteur de table d’intégration ou une fonction de table, les données sont récupérées par le serveur ClickHouse :
Jusqu’à ce que les données soient entièrement chargées, le serveur exécute une boucle :
min_insert_block_size_rows(par défaut :1048545lignes)min_insert_block_size_bytes(par défaut :256 MiB)
min_insert_block_size_bytes désigne la taille du bloc en mémoire non compressé (et non la taille de la part compressée sur disque). Notez également que les blocs et parts créés contiennent rarement exactement le nombre configuré de lignes ou d’octets, car ClickHouse traite les données sous forme de flux et les traite bloc par bloc de lignes. Par conséquent, ces paramètres définissent des seuils minimums.
Gardez à l’esprit les fusions
MergeTree).
Parallélisme des insertions
max_insert_threads. La valeur par défaut est de 1 pour ClickHouse open-source et de 4 pour ClickHouse Cloud.
Avec un grand nombre de fichiers, le traitement en parallèle par plusieurs threads d’insertion fonctionne bien. Il peut saturer complètement à la fois les CPU cores disponibles et la bande passante réseau (pour les téléchargements parallèles de fichiers). Dans les scénarios où seules quelques gros fichiers sont chargés dans une table, ClickHouse met automatiquement en place un niveau élevé de parallélisme pour le traitement des données et optimise l’utilisation de la bande passante réseau en lançant des threads de lecture supplémentaires par thread d’insertion afin de lire (télécharger) davantage de plages distinctes au sein des gros fichiers en parallèle.
Pour la fonction et la table s3, le téléchargement en parallèle d’un fichier individuel est déterminé par les valeurs max_download_threads et max_download_buffer_size. Les fichiers ne sont téléchargés en parallèle que si leur taille est supérieure à 2 * max_download_buffer_size. Par défaut, max_download_buffer_size est défini sur 10MiB. Dans certains cas, vous pouvez sans risque augmenter cette taille de tampon à 50 MB (max_download_buffer_size=52428800) afin de garantir que chaque fichier soit téléchargé par un seul thread. Cela peut réduire le temps que chaque thread consacre aux appels S3 et donc aussi diminuer le temps d’attente sur S3. En outre, pour les fichiers trop petits pour une lecture en parallèle, afin d’augmenter le throughput, ClickHouse précharge automatiquement les données en lisant ces fichiers à l’avance de manière asynchrone.
Mesurer les performances
Impact des ressources matérielles
- la taille initiale des parts prise en charge
- le niveau possible de parallélisme des insertions
- le débit des fusions de parts en arrière-plan
Proximité régionale
Formats
s3 et du moteur S3. Pour la lecture de fichiers bruts, certains de ces formats présentent des avantages spécifiques :
- Les formats qui incluent les noms de colonnes, comme Native, Parquet, CSVWithNames et TabSeparatedWithNames, rendent les requêtes moins verbeuses, car l’utilisateur n’a pas besoin de préciser le nom de colonne dans la fonction
s3. Les noms de colonnes permettent en effet d’inférer cette information. - Les formats diffèrent aussi en termes de performances de lecture et d’écriture. Native et Parquet sont les formats les plus performants en lecture, car ils sont déjà orientés colonnes et plus compacts. Le format Native bénéficie en outre de son alignement avec la façon dont ClickHouse stocke les données en mémoire, ce qui réduit la surcharge de traitement lorsque les données sont transmises en flux vers ClickHouse.
- La taille des blocs a souvent un impact sur la latence de lecture des gros fichiers. Cela est particulièrement visible si vous ne faites qu’échantillonner les données, par exemple pour renvoyer les N premières lignes. Avec des formats comme CSV et TSV, les fichiers doivent être analysés pour renvoyer un ensemble de lignes. Des formats comme Native et Parquet permettent donc un échantillonnage plus rapide.
- Chaque format de compression présente des avantages et des inconvénients, en trouvant souvent un compromis entre niveau de compression et vitesse, avec un biais en faveur des performances de compression ou de décompression. Si vous compressez des fichiers bruts comme CSV ou TSV, lz4 offre les meilleures performances de décompression, au détriment du niveau de compression. Gzip compresse généralement mieux, au prix de vitesses de lecture légèrement inférieures. Xz va encore plus loin en offrant généralement la meilleure compression, mais avec les performances de compression et de décompression les plus lentes. En cas d’export, Gz et lz4 offrent des vitesses de compression comparables. Mettez cela en balance avec vos vitesses de connexion. Tout gain lié à une compression ou une décompression plus rapide sera facilement annulé par une connexion plus lente à vos buckets S3.
- Des formats comme Native ou Parquet ne justifient généralement pas la surcharge liée à la compression. Les gains en taille de données sont souvent minimes, car ces formats sont intrinsèquement compacts. Le temps passé à compresser et décompresser compensera rarement les temps de transfert réseau, d’autant plus que S3 est disponible mondialement avec une bande passante réseau élevée.
Jeu de données d’exemple
SELECT qui renvoient de grands volumes de données au client, utilisez soit le format Null pour les requêtes, soit dirigez les résultats vers le moteur Null. Cela permet d’éviter que le client soit submergé de données et de saturer le réseau.
Lors de l’exécution de requêtes en lecture, la requête initiale peut souvent sembler plus lente que les exécutions suivantes de la même requête. Cela peut s’expliquer à la fois par le cache propre à S3 et par le cache d’inférence de schéma de ClickHouse. Celui-ci stocke le schéma inféré des fichiers, ce qui permet d’ignorer l’étape d’inférence lors des accès suivants et donc de réduire le temps de requête.
Utilisation des threads pour les lectures
- En général, la valeur par défaut de
max_threadsest suffisante, c.-à-d. le nombre de cœurs. Si la quantité de mémoire utilisée par une requête est élevée et doit être réduite, ou si leLIMITsur les résultats est faible, cette valeur peut être diminuée. Les utilisateurs disposant de beaucoup de mémoire peuvent essayer d’augmenter cette valeur pour obtenir éventuellement un meilleur débit de lecture depuis S3. En règle générale, cela n’est utile que sur des machines avec peu de cœurs, c.-à-d. < 10. Le bénéfice d’une parallélisation supplémentaire diminue généralement lorsque d’autres ressources deviennent un goulot d’étranglement, par exemple le réseau et la contention CPU. - Les versions de ClickHouse antérieures à 22.3.1 ne parallélisaient les lectures entre plusieurs fichiers que lors de l’utilisation de la fonction
s3ou du moteur de tableS3. L’utilisateur devait alors s’assurer que les fichiers étaient découpés en fragments sur S3 et lus à l’aide d’un motif glob afin d’obtenir des performances de lecture optimales. Les versions ultérieures parallélisent désormais les téléchargements au sein d’un même fichier. - Dans les scénarios avec peu de threads, vous pouvez tirer parti du paramètre
remote_filesystem_read_methoddéfini sur “read” afin de forcer la lecture synchrone des fichiers depuis S3. - Pour la fonction
s3et le moteur de tableS3, le téléchargement parallèle d’un fichier individuel est déterminé par les valeurs demax_download_threadsetmax_download_buffer_size. Bien quemax_download_threadscontrôle le nombre de threads utilisés, les fichiers ne seront téléchargés en parallèle que si leur taille est supérieure à 2 *max_download_buffer_size. Par défaut,max_download_buffer_sizeest défini sur 10MiB. Dans certains cas, vous pouvez augmenter sans risque cette taille de tampon à 50 MB (max_download_buffer_size=52428800), afin de vous assurer que les fichiers plus petits ne sont téléchargés que par un seul thread. Cela peut réduire le temps que chaque thread passe à effectuer des appels S3 et donc aussi diminuer le temps d’attente sur S3. Voir ce billet de blog pour un exemple.
system.query_log.
Reprenons notre requête précédente : doubler max_threads à 16 (la valeur par défaut de max_thread est le nombre de cœurs sur un nœud) améliore de 2x les performances de notre requête de lecture, au prix d’une consommation mémoire plus élevée. Augmenter davantage max_threads produit des rendements décroissants, comme illustré.
Réglage des threads et de la taille de bloc pour les inserts
- Plus la taille du bloc d’insertion configurée est grande, moins ClickHouse doit créer de parts, et moins il faut d’E/S de fichiers disque et de fusions en arrière-plan.
- Plus le nombre de threads d’insert parallèles configuré est élevé, plus les données seront traitées rapidement.
min_insert_block_size_rows sur 0 (pour désactiver le seuil basé sur le nombre de lignes), tout en définissant max_insert_threads sur la valeur choisie et min_insert_block_size_bytes sur le résultat calculé à l’aide de la formule ci-dessus.
Voici comment utiliser cette formule avec notre exemple précédent de Stack Overflow.
max_insert_threads=4(8 cœurs par nœud)peak_memory_usage_in_bytes- 32 GiB (100 % des ressources du nœud) ou34359738368octets.min_insert_block_size_bytes=34359738368/(3*4) = 2863311530
insert de plus de 33%. Nous laissons au lecteur le soin de voir s’il peut encore améliorer les performances sur un seul nœud.
Mise à l’échelle par les ressources et les nœuds
Mise à l’échelle verticale
Des nœuds individuels peuvent également être confrontés à des goulots d’étranglement au niveau du réseau et des requêtes GET vers S3, ce qui empêche une progression linéaire des performances lors d’une mise à l’échelle verticale.
Mise à l’échelle horizontale
s3Cluster, comme décrit dans Utilisation des clusters. Cela permet de répartir les lectures entre les nœuds.
Le serveur qui reçoit initialement la requête d’insertion résout d’abord le motif glob, puis répartit dynamiquement le traitement de chaque fichier correspondant entre lui-même et les autres serveurs.
Nous reprenons notre précédente requête de lecture en répartissant la charge de travail sur 3 nœuds, en ajustant la requête pour utiliser s3Cluster. Dans ClickHouse Cloud, cela se fait automatiquement en faisant référence au cluster default.
Comme indiqué dans Utilisation des clusters, ce travail est réparti au niveau des fichiers. Pour bénéficier de cette fonctionnalité, vous devez disposer d’un nombre suffisant de fichiers, c.-à-d. d’un nombre au moins supérieur à celui des nœuds.
s3Cluster, les insertions s’effectuent sur le nœud initiateur. Cela signifie que, même si les lectures s’exécutent sur chaque nœud, les lignes résultantes seront renvoyées vers l’initiateur pour y être distribuées. Dans les scénarios à haut débit, cela peut constituer un goulot d’étranglement. Pour y remédier, définissez le paramètre parallel_distributed_insert_select pour la fonction s3cluster.
Le paramétrer sur parallel_distributed_insert_select=2 garantit que les opérations SELECT et INSERT seront exécutées sur chaque shard, depuis/vers la table sous-jacente du moteur Distributed sur chaque nœud.
Ajustements supplémentaires
Désactiver la déduplication
INSERT INTO SELECT depuis un stockage d’objets. En désactivant cette fonctionnalité au moment de l’insertion, il est possible d’améliorer les performances, comme illustré ci-dessous :
Optimiser lors de l’insertion
optimize_on_insert contrôle si les parts de données sont fusionnées pendant le processus d’insertion. Lorsqu’il est activé (optimize_on_insert = 1 par défaut), les petites parts sont fusionnées en parts plus grandes au moment de leur insertion, ce qui améliore les performances des requêtes en réduisant le nombre de parts à lire. Cependant, cette fusion ajoute un surcoût au processus d’insertion, ce qui peut ralentir les insertions à haut débit.
La désactivation de ce paramètre (optimize_on_insert = 0) évite la fusion pendant les insertions, ce qui permet d’écrire les données plus rapidement, en particulier en cas de petites insertions fréquentes. Le processus de fusion est alors reporté en arrière-plan, ce qui améliore les performances d’insertion, mais augmente temporairement le nombre de petites parts, ce qui peut ralentir les requêtes jusqu’à la fin de la fusion en arrière-plan. Ce paramètre est idéal lorsque les performances d’insertion sont prioritaires et que le processus de fusion en arrière-plan pourra effectuer l’optimisation efficacement par la suite. Comme indiqué ci-dessous, la désactivation de ce paramètre peut améliorer le débit d’insertion :
Remarques diverses
- Dans les environnements à mémoire limitée, envisagez de réduire
max_insert_delayed_streams_for_parallel_writesi vous effectuez des insertions dans S3.