Partager via


Comprendre les performances des requêtes Direct Lake

Outre la complexité de la conception et de la requête du modèle sémantique, les performances direct Lake dépendent spécifiquement des tables Delta bien ajustées pour un chargement efficace et rapide de colonnes (transcodage) et une exécution de requête optimale. Veillez à appliquer l’optimisation V-Order. En outre, conservez le nombre de fichiers Parquet petits, utilisez de grands groupes de lignes et essayez de réduire l’effet des mises à jour des données sur le journal Delta. Il s’agit des meilleures pratiques courantes qui peuvent aider à garantir l’exécution rapide des requêtes en modes froid, semi-chaud, chaud et Direct Lake chaud.

Cet article explique comment les performances de Direct Lake dépendent de l’intégrité de la table Delta et des mises à jour efficaces des données. Comprendre ces dépendances est cruciale. Vous découvrez que la disposition des données dans vos fichiers Parquet est aussi importante pour les performances des requêtes qu'une bonne conception de modèle sémantique et des expressions d'analyse de données (DAX) bien réglées.

Ce que vous devez savoir

Cet article suppose que vous connaissez déjà les concepts suivants :

  • Tables delta dans OneLake : des informations détaillées sur les tables Delta dans OneLake sont disponibles dans la documentation de base de Microsoft Fabric.
  • Fichiers Parquet, groupes de lignes et journal Delta : le format de fichier Parquet, y compris la façon dont les données sont organisées en groupes de lignes et en blocs de colonnes, et comment les métadonnées sont gérées, est expliquée dans la documentation du format de fichier Parquet. Consultez également la documentation du protocole du journal des transactions Delta.
  • Optimisation de la table Delta et V-Order : consultez la documentation Fabric Lakehouse sur la maintenance des tables Delta, comme l’optimisation de la table Delta Lake et V-Order.
  • L’encadrement et le transcodage : l’actualisation d’un modèle sémantique Direct Lake (trame) et le chargement des données de colonne à la demande (transcodage) sont des concepts importants, abordés au niveau d’introduction dans l’article de vue d’ensemble de Direct Lake.
  • Moteur de formule et de stockage : lorsqu’une requête DAX est exécutée, le moteur de formule génère le plan de requête et récupère les données et les agrégations initiales nécessaires à partir du moteur de stockage. Les optimisations décrites dans cet article se concentrent sur le moteur de stockage. Pour en savoir plus sur le Moteur de Formule et le Moteur de Stockage, explorez la documentation pour développeurs Analysis Services.
  • VertiPaq et VertiScan : en mode importation et en mode Direct Lake, le moteur de stockage utilise son moteur VertiPaq pour conserver un stockage en mémoire de type colonne. VertiScan permet au moteur de formule d’interagir avec VertiPaq. Pour plus d’informations, consultez la documentation du développeur Analysis Services.
  • Encodage de dictionnaire : les fichiers Parquet et VertiPaq utilisent l’encodage de dictionnaire, qui est une technique de compression de données appliquée à des colonnes individuelles de différents types de données, telles que int, long, date et char. Le système fonctionne en stockant chaque valeur unique de la colonne en mémoire sous forme d'entier, à l'aide de l'encodage de la longueur de course (RLE) et de l'encodage hybrideBit-Packing. VertiPaq utilise toujours l’encodage de dictionnaire, mais Parquet peut basculer vers l’encodage brut ou l’encodage delta dans certaines circonstances, comme expliqué dans la documentation parquet File-Format, ce qui exigerait que Direct Lake réencode les données avec un effet correspondant sur les performances de transcodage.
  • Segments de colonne et segments de colonne : reportez-vous à la façon dont Parquet et VertiPaq stockent les données de colonne pour une récupération efficace des données. Chaque colonne d’une table est divisée en blocs plus petits qui peuvent être traités et compressés indépendamment. VertiPaq appelle ces morceaux des segments. Vous pouvez utiliser l’ensemble de lignes de schéma DISCOVER_STORAGE_TABLE_COLUMN_SEGMENTS pour récupérer des informations sur les segments de colonne dans un modèle sémantique Direct Lake.
  • Notebooks Python et Jupyter : Les notebooks Jupyter fournissent un environnement interactif pour écrire et exécuter du code Python. Les connaissances de base de Python sont utiles si vous souhaitez suivre les extraits de code plus loin dans ce chapitre. Pour plus d’informations, consultez la référence du langage Python. Pour plus d’informations sur l’utilisation de notebooks dans Microsoft Fabric, consultez Comment utiliser des notebooks - Microsoft Fabric.

Ce qui affecte les performances des requêtes Direct Lake

Cette section récapitule les principaux facteurs affectant les performances de Direct Lake. Les sections suivantes offrent des explications plus détaillées :

  • V-Order compression : l’efficacité de la compression peut affecter les performances des requêtes, car une meilleure compression entraîne un chargement plus rapide des données et un traitement des requêtes plus efficace. Le chargement des données est rapide, car la diffusion en continu de données compressées améliore l’efficacité du transcodage. Les performances des requêtes sont également optimales, car la compression V-Order permet à VertiScan de calculer les résultats directement sur les données compressées, en ignorant l’étape de décompression.
  • Types de données : l’utilisation de types de données appropriés pour les colonnes peut améliorer la compression et les performances. Par exemple, utilisez des types de données entiers au lieu de chaînes dans la mesure du possible et évitez de stocker des entiers sous forme de chaînes.
  • Taille et nombre de segments : VertiPaq stocke les données de colonne dans les segments. Un grand nombre de segments plus petits peuvent affecter négativement les performances des requêtes. Pour les tables volumineuses, Direct Lake préfère les grandes tailles de segments, comme entre 1 million et 16 millions de lignes.
  • Cardinalité de colonne : les colonnes de cardinalité élevée (colonnes avec de nombreuses valeurs uniques) peuvent ralentir les performances. Réduire la cardinalité si possible peut aider.
  • Index et agrégations : les colonnes avec une cardinalité inférieure bénéficient de l’encodage de dictionnaire, ce qui peut accélérer les requêtes en réduisant la quantité de données à analyser.
  • Repli DirectQuery : les opérations de repli peuvent entraîner des performances de requête plus lentes, car les données doivent désormais être récupérées de l'extrémité d’analyse SQL de la source de données Fabric. De plus, le repli s'appuie sur des plans de requête hybrides pour prendre en charge DirectQuery et VertiScan avec des compromis sur les performances, même lorsque Direct Lake n’a pas besoin de recourir au repli. Si possible, désactivez le repli DirectQuery pour éviter les plans de requête hybrides.
  • Degré de résidence de mémoire : les modèles sémantiques Direct Lake peuvent être en état froid, semi-chaud, chaud ou très chaud avec des performances de plus en plus optimisées de froid à très chaud. La transition rapide du froid au chaud est une clé pour une bonne performance Direct Lake.
    • Froid : le magasin VertiPaq est vide. Toutes les données requises pour répondre à une requête DAX doivent être chargées à partir de tables Delta.
    • Semiwarm : Direct Lake supprime uniquement les segments de colonne pendant le cadrage qui appartiennent à des groupes de lignes qui ont été supprimés. Cela signifie que seules les données mises à jour ou nouvellement ajoutées doivent être chargées. Un modèle sémantique Direct Lake peut également entrer dans l’état semi-chaud sous pression de la mémoire lorsqu’il doit décharger des segments et des index de jointure à cause de cette pression.
    • Chaud : les données de colonne requises pour répondre à une requête DAX sont déjà entièrement chargées en mémoire.
    • Chaud : les données de colonne sont déjà entièrement chargées en mémoire, les caches VertiScan sont renseignés et les requêtes DAX atteignent les caches.
  • Sollicitation de la mémoire : Direct Lake doit charger toutes les données de colonne requises pour répondre à une requête DAX en mémoire, ce qui peut épuiser les ressources de mémoire disponibles. Avec une mémoire insuffisante, Direct Lake doit décharger les données de colonne précédemment chargées, que Direct Lake peut alors devoir recharger à nouveau pour les requêtes DAX suivantes. Le dimensionnement adéquat des modèles sémantiques Direct Lake peut aider à éviter le rechargement fréquent.

Résidence en mémoire et performance des requêtes

Direct Lake fonctionne le mieux dans l’état chaud ou chaud, tandis que les états froids entraînent des performances plus lentes. Direct Lake évite de revenir au froid autant que possible en utilisant le cadrage incrémentiel.

Amorçage

Après la charge initiale du modèle sémantique, aucune donnée de colonne n’est encore résidente en mémoire. Direct Lake est froid. Lorsqu’un client envoie une requête DAX à un modèle sémantique Direct Lake dans l’état froid, Direct Lake doit effectuer les tâches principales suivantes afin que la requête DAX puisse être traitée et répondue :

  • Transcodage du dictionnaire VertiPaq. Direct Lake doit fusionner les dictionnaires Parquet locaux pour chaque segment de colonne afin de créer un dictionnaire VertiPaq global pour la colonne. Cette opération de fusion affecte le temps de réponse des requêtes.

  • Chargement de morceaux de colonne Parquet dans des segments de colonne. Dans la plupart des cas, il s'agit d'une transformation directe des ID de données Parquet vers des ID VertiPaq lorsque les deux parties peuvent utiliser l'encodage hybride RLE/Bit-Packing. Si les dictionnaires Parquet utilisent un encodage brut, VertiPaq doit convertir les valeurs en RLE/Bit-Packing encodage hybride, ce qui prend plus de temps.

    • Les performances de Direct Lake sont optimales sur les fichiers Parquet triés sur V, car V-Ordering augmente la qualité de la compression RLE. Direct Lake peut charger des données ordonnées V étroitement emballées plus rapidement que les données moins compressées.
  • Génération d’index de jointure. Si la requête DAX accède aux colonnes de plusieurs tables, Direct Lake doit générer des index de jointure en fonction des relations de table afin que VertiScan puisse joindre correctement les tables. Pour générer les index de jointure, Direct Lake doit charger les dictionnaires des colonnes clés participant à la relation et les segments de colonne de la colonne clé primaire (la colonne du côté Un de la relation de table).

  • Application de vecteurs de suppression Delta. Si une table Delta source utilise des vecteurs de suppression, Direct Lake doit charger ces vecteurs de suppression pour garantir que les données supprimées sont exclues du traitement des requêtes.

    Remarque

    L’état froid peut également être induit en envoyant une commande XMLA processClear suivie par une commande processFull au modèle. La ProcessClear commande supprime toutes les données et l’association avec la version de table Delta encadrée. La commande ProcessFull XMLA exécute une structuration pour associer le modèle à la dernière version de commit Delta disponible.

Trame incrémentielle

Lors de la définition du cadre, Direct Lake analyse le journal Delta de chaque table Delta et ne supprime les segments de colonnes chargés et les index de jointure que lorsque les données sous-jacentes ont été modifiées. Les dictionnaires sont conservés pour éviter le transcodage inutile et les nouvelles valeurs sont simplement ajoutées aux dictionnaires existants. Cette approche de cadrage incrémentiel réduit la charge de rechargement et améliore les performances des requêtes à froid.

Vous pouvez analyser l’efficacité de l’encadrement incrémentiel à l’aide de la fonction DAX INFO.STORAGETABLECOLUMNSEGMENTS(), qui encapsule l'DISCOVER_STORAGE_TABLE_COLUMN_SEGMENTS ensemble de lignes du schéma. Procédez comme suit pour garantir des résultats significatifs :

  1. Interrogez votre modèle sémantique Direct Lake pour vous assurer qu'il est dans un état tiède ou chaud.
  2. Mettez à jour la table Delta que vous souhaitez examiner et actualiser le modèle sémantique Direct Lake pour effectuer l’encadrement.
  3. Exécutez une requête DAX pour récupérer les informations de segment de colonne à l’aide de la INFO.STORAGETABLECOLUMNSEGMENTS() fonction, comme dans la capture d’écran suivante. La capture d’écran utilise un petit exemple de tableau à des fins d’illustration. Chaque colonne n’a qu’un seul segment. Les segments ne résident pas dans la mémoire. Cela indique un état froid réel. Si le modèle était chaud avant l’enchâssement, cela signifie que la table Delta a été mise à jour en utilisant une méthode de chargement de données destructrice, ce qui rend impossible l’utilisation de la structuration incrémentielle. Les modèles de mise à jour de table delta sont abordés plus loin dans cet article.

Capture d’écran montrant le résultat d’une requête DAX à l’aide d’INFO. STORAGETABLECOLUMNSEGMENTS dans un modèle sémantique Direct Lake, mettant en évidence la résidence des segments de colonne.

Remarque

Lorsqu’une table Delta ne reçoit aucune mise à jour, aucun rechargement n’est nécessaire pour les colonnes qui résident déjà en mémoire. Lors de l’utilisation de modèles de mise à jour non destructeurs, les requêtes affichent beaucoup moins d’effets de performances après l’encadrement, car l’encadrement incrémentiel permet essentiellement à Direct Lake de mettre à jour des parties substantielles des données en mémoire existantes en place.

Résidence en mémoire complète

Avec les dictionnaires, les segments de colonne et les index de jointure chargés, Direct Lake atteint son état optimal avec des performances de requêtes équivalentes à celles du mode d'importation. Dans les deux modes, le nombre et la taille des segments de colonne jouent un rôle crucial dans l’optimisation des performances des requêtes.

Différences entre les tables delta

Les fichiers Parquet organisent les données par colonnes plutôt que par lignes. Direct Lake organise également les données par colonnes. L’alignement facilite l’intégration transparente, mais il existe des différences importantes, en particulier concernant les groupes de lignes et les dictionnaires.

Groupes de lignes et segments de colonne

Un groupe de lignes dans un fichier Parquet se compose de blocs de colonnes, et chaque bloc contient des données pour une colonne spécifique. Un segment de colonne dans un modèle sémantique, d’autre part, contient également un segment de données de colonne.

Il existe une relation directe entre le nombre total de groupes de lignes d’une table Delta et le nombre de segments pour chaque colonne de la table de modèle sémantique correspondante. Par exemple, si une table Delta sur tous ses fichiers Parquet actuels comporte trois groupes de lignes au total, la table de modèle sémantique correspondante comporte trois segments par colonne, comme illustré dans le diagramme suivant. En d’autres termes, si une table Delta a un grand nombre de petits groupes de lignes, une table de modèle sémantique correspondante aurait également un grand nombre de segments de colonne minuscules. Cela affecterait négativement les performances des requêtes.

Diagramme montrant la relation entre les groupes de lignes de table Delta et les segments de colonne de modèle sémantique dans Direct Lake.

Remarque

Étant donné que Direct Lake préfère les segments de colonnes volumineux, les groupes de lignes des tables Delta sources doivent idéalement être volumineux.

Dictionnaires locaux et dictionnaire global

Le nombre total de groupes de lignes d’une table Delta a également un effet direct sur les performances de transcodage du dictionnaire, car les fichiers Parquet utilisent des dictionnaires locaux tandis que les modèles sémantiques Direct Lake utilisent un dictionnaire global pour chaque colonne, comme illustré dans le diagramme suivant. Plus le nombre de groupes de lignes est élevé, plus le nombre de dictionnaires locaux que Direct Lake doit fusionner pour créer un dictionnaire global, et plus le transcodage est long.

Diagramme illustrant le processus de fusion des dictionnaires Parquet locaux dans un dictionnaire global pour les modèles sémantiques Direct Lake.

Modèles de mise à jour de table delta

La méthode utilisée pour ingérer des données dans une table Delta peut considérablement influencer l’efficacité du cadrage incrémentiel. Par exemple, l’utilisation de l’option Overwrite lors du chargement de données dans une table existante efface le journal Delta à chaque chargement. Cela signifie que Direct Lake ne peut pas utiliser le framing incrémentiel et doit recharger toutes les données, dictionnaires et index de jointure. Ces modèles de mise à jour destructrice affectent négativement les performances des requêtes.

Diagramme montrant les modèles d’ingestion et de mise à jour des données pour les tables Delta dans Direct Lake.

Cette section traite des modèles de mise à jour des tables Delta qui permettent à Direct Lake d’utiliser le traitement par tranches incrémentielles, en préservant les éléments du magasin de colonnes VertiPaq tels que les dictionnaires, les segments de colonne et les index de jointure, pour optimiser l’efficacité du transcodage et accroître les performances des requêtes froides.

Traitement par lots sans partitionnement

Ce modèle de mise à jour collecte et traite les données par lots volumineux à intervalles planifiés, par exemple chaque semaine ou mensuelle. À mesure que de nouvelles données arrivent, les anciennes données sont souvent supprimées d'une manière de fenêtre déroulante ou glissante pour garder la taille de la table sous contrôle. Toutefois, la suppression d’anciennes données peut être un défi si les données sont réparties sur la plupart des fichiers Parquet. Par exemple, la suppression d’un jour sur 30 jours peut affecter 95% des fichiers Parquet au lieu de 5%. Dans ce cas, Direct Lake doit recharger 95% des données même pour une opération de suppression relativement petite. Le même problème s’applique également aux mises à jour des lignes existantes, car les mises à jour sont combinées des suppressions et des ajouts. Vous pouvez analyser l’effet des opérations de suppression et de mise à jour à l’aide de Delta Analyzer, comme expliqué plus loin dans cet article.

Traitement par lots avec partitionnement

Le partitionnement de table delta peut contribuer à réduire l’effet des opérations de suppression , car la table est divisée en fichiers Parquet plus petits stockés dans des dossiers en fonction des valeurs distinctes de la colonne de partition. Les colonnes de partition couramment utilisées incluent la date, la région ou d’autres catégories dimensionnelles. Dans l’exemple précédent de suppression d’un jour sur 30 jours, une table Delta partitionnée par date limite les suppressions uniquement aux fichiers Parquet de la partition pour ce jour. Toutefois, il est important de noter que le partitionnement étendu peut entraîner un nombre sensiblement accru de fichiers et de groupes de lignes Parquet, ce qui entraîne une augmentation excessive des segments de colonne dans le modèle sémantique Direct Lake, ce qui affecte négativement les performances des requêtes. Le choix d’une colonne de partition à faible cardinalité est essentiel pour les performances des requêtes. Comme meilleure pratique, la colonne doit avoir moins de 100 à 200 valeurs distinctes.

Chargement incrémentiel

Avec le chargement incrémentiel, le processus de mise à jour insère uniquement de nouvelles données dans une table Delta sans affecter les fichiers Parquet et les groupes de lignes existants. Il n’y a aucune suppression. Direct Lake peut charger les nouvelles données de façon incrémentielle sans avoir à supprimer et recharger les éléments de magasin de colonnes VertiPaq existants. Cette méthode fonctionne bien avec le cadrage incrémentiel Direct Lake. Le partitionnement de table delta n’est pas nécessaire.

Traitement des flux de données

Le traitement des données en quasi temps réel, à mesure qu’elles arrivent, peut entraîner une prolifération de petits fichiers et de groupes de lignes Parquet, ce qui peut affecter négativement les performances de Direct Lake. Comme avec le modèle de chargement incrémentiel, il n’est pas nécessaire de partitionner la table Delta. Toutefois, la maintenance fréquente des tables est essentielle pour s’assurer que le nombre de fichiers et de groupes de lignes Parquet reste dans les limites de garde-fou spécifiées dans l’article de vue d’ensemble de Direct Lake. En d’autres termes, n’oubliez pas d’exécuter Spark Optimize régulièrement, comme quotidiennement ou encore plus souvent. Spark Optimize est de nouveau abordé dans la section suivante.

Remarque

L’analyse en temps réel est mieux réalisée avec Eventstreams, des bases de données KQL et Eventhouse. Pour obtenir des conseils, consultez la documentationReal-Time Intelligence dans Microsoft Fabric .

Maintenance de la table Delta

Les tâches de maintenance clés incluent le nettoyage et l’optimisation des tables Delta. Pour automatiser les opérations de maintenance, vous pouvez utiliser les API Lakehouse, comme expliqué dans la documentation gérer lakehouse avec l’API REST Microsoft Fabric .

Passer l'aspirateur

Le processus de "Vacuuming" supprime les fichiers Parquet qui ne sont plus inclus dans la version de commit Delta actuelle et qui sont antérieurs à un seuil de rétention défini. La suppression de ces fichiers Parquet n’affecte pas les performances de Direct Lake, car Direct Lake charge uniquement les fichiers Parquet qui se trouvent dans la version de validation actuelle. Si vous exécutez VACUUM quotidiennement avec les valeurs par défaut, les versions de validation Delta des sept derniers jours sont conservées pour le voyage dans le temps.

Important

Étant donné qu’un modèle sémantique Direct Lake encadré fait référence à une version de validation Delta particulière, vous devez vous assurer que la table Delta conserve cette version jusqu’à ce que vous actualisiez à nouveau (frame) le modèle pour le déplacer vers la version actuelle. Sinon, les utilisateurs rencontrent des erreurs de requête lorsque le modèle sémantique Direct Lake tente d’accéder aux fichiers Parquet qui n’existent plus.

Spark Optimize

L’optimisation de la table delta fusionne plusieurs petits fichiers Parquet en moins de fichiers volumineux. Étant donné que cela peut affecter les performances de Direct Lake froid, il est recommandé d'optimiser peu fréquemment, comme pendant le week-end ou à la fin du mois. Procédez à des optimisations plus fréquentes si de petits fichiers Parquet s’accumulent rapidement (petites mises à jour à haute fréquence) afin de garantir que la table Delta respecte les seuils de sécurité.

Le partitionnement peut aider à réduire l’effet d’optimisation sur l’encadrement incrémentiel, car le partitionnement colocalise efficacement les données. Par exemple, le partitionnement d'une grande table Delta basé sur une colonne date_key à faible cardinalité restreindrait la maintenance hebdomadaire à un maximum de sept partitions. La table Delta conserverait la plupart des fichiers Parquet existants. Direct Lake n’aurait qu’à recharger sept jours de données.

Analyse des mises à jour des tables Delta

Utilisez Delta Analyzer ou des outils similaires pour étudier comment les mises à jour de table Delta affectent les fichiers et les groupes de lignes Parquet. Delta Analyzer vous permet de suivre l’évolution des fichiers Parquet, des groupes de lignes, des blocs de colonnes et des colonnes en réponse aux opérations d’ajout, de mise à jour et de suppression . Delta Analyzer est disponible sous forme de notebook Jupyter autonome. Il est également disponible dans la bibliothèque semantic-link-labs. Les sections suivantes utilisent semantic-link-labs. Cette bibliothèque est facile à installer dans un notebook à l’aide de la %pip install semantic-link-labs commande.

Taille du groupe de lignes

La taille de groupe de lignes idéale pour les modèles sémantiques Direct Lake est comprise entre 1 million et 16 millions de lignes, mais Fabric peut utiliser des tailles de groupe de lignes plus grandes pour les tables volumineuses si les données sont compressibles. En règle générale, nous vous déconseillons de modifier la taille du groupe de lignes par défaut. Il est préférable de laisser Fabric gérer la disposition de la table Delta. Mais c’est aussi une bonne idée de vérifier.

Le code Python suivant peut servir de point de départ pour analyser les tailles de groupes de lignes et d’autres détails d’une table Delta dans un lakehouse connecté à un notebook Fabric. Le tableau suivant montre la sortie d’un exemple de table avec 1 milliard de lignes.

import sempy_labs as labs
from IPython.display import HTML
from IPython.display import clear_output

table_name = "<Provide your table name>"

# Load the Delta table and run Delta Analyzer
df = spark.read.format("delta").load(f"Tables/{table_name}")
da_results = labs.delta_analyzer(table_name)

# Display the table summary in an HTML table.
clear_output()

df1 = da_results['Summary'].iloc[0]

html_table = "<table border='1'><tr><th>Column Name</th><th>{table_name}</th></tr>"
for column in da_results['Summary'].columns:
    html_table += f"<tr><td>{column}</td><td>{df1[column]}</td></tr>"
html_table += "</table>"

display(HTML(html_table))

Sortie :

Paramètre Valeur
Nom de la table sales_1
Nombre de lignes 1000000000
Groupes de lignes Vingt-quatre
Fichiers Parquet 8
Nombre maximal de lignes par groupe de lignes 51210000
Nombre minimal de lignes par groupe de lignes 22580000
Nombre moyen de lignes par groupe de lignes 41666666.666666664
VOrder activé Vrai
Taille totale 7700808430
Horodatage 2025-03-24 03:01:02.794979

Le résumé delta Analyzer affiche une taille moyenne de groupe de lignes d’environ 40 millions de lignes. Cela est supérieur à la taille maximale recommandée du groupe de lignes de 16 millions de lignes. Heureusement, la taille plus importante du groupe de lignes n’entraîne pas de problèmes notables pour Direct Lake. Les groupes de lignes plus grands facilitent le traitement continu des segments avec une surcharge minimale dans le moteur de stockage. À l’inverse, de petits groupes de lignes, ceux qui sont nettement inférieurs à 1 million de lignes, peuvent provoquer des problèmes de performances.

Ce qui est plus important dans l’exemple précédent, c’est que Fabric a distribué les groupes de lignes sur huit fichiers Parquet. Cela s'aligne avec la capacité du Fabric à supporter efficacement des opérations de lecture parallèle grâce au nombre de cœurs. Il est également important que les tailles de groupe de lignes individuelles ne s’écartent pas trop de la moyenne. De grandes variations peuvent entraîner une charge VertiScan non uniforme, ce qui entraîne des performances de requête moins optimales.

Mises à jour de fenêtres glissantes

À des fins d’illustration, l’exemple de code Python suivant simule une mise à jour de fenêtre glissante. Le code supprime les lignes avec le DateID le plus ancien d'une table Delta d'exemple. Il met ensuite à jour le DateID de ces lignes et les insère à nouveau dans l’exemple de table en tant que lignes les plus récentes.

from pyspark.sql.functions import lit

table_name = "<Provide your table name>"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")

# Get the rows of the oldest DateID.
rows_df = table_df[table_df["DateID"] == 20200101]
rows_df = spark.createDataFrame(rows_df.rdd, schema=rows_df.schema)

# Delete these rows from the table
table_df.createOrReplaceTempView(f"{table_name}_view")
spark.sql(f"DELETE From {table_name}_view WHERE DateID = '20200101'")

# Update the DateID and append the rows as new data
rows_df = rows_df.withColumn("DateID", lit(20250101))
rows_df.write.format("delta").mode("append").save(f"Tables/{table_name}")

La get_delta_table_history fonction de la bibliothèque semantic-link-labs peut aider à analyser l’effet de cette mise à jour de fenêtre coulissante. Consultez l’exemple de code Python suivant. Consultez également la table avec la sortie après l’extrait de code.

import sempy_labs as labs
from IPython.display import HTML
from IPython.display import clear_output

table_name = "<Provide your table name>"
da_results = labs.get_delta_table_history(table_name)

# Create a single HTML table for specified columns
html_table = "<table border='1'>"
# Add data rows for specified columns
for index, row in da_results.iterrows():
    for column in ['Version', 'Operation', 'Operation Parameters', 'Operation Metrics']:
        if column == 'Version':
            html_table += f"<tr><td><b>Version</b></td><td><b>{row[column]}</b></td></tr>"
        else:
            html_table += f"<tr><td>{column}</td><td>{row[column]}</td></tr>"
html_table += "</table>"

# Display the HTML table
display(HTML(html_table))

Sortie :

Version Descriptif Valeur
2 Fonctionnement ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '548665', 'numOutputBytes' : '4103076'}
1 Fonctionnement Supprimer
Paramètres d’opération {'predicate' : '["(DateID#3910 = 20200101)"]'}
Métriques d’opération {'numRemovedFiles' : '8', 'numRemovedBytes' : '7700855198', 'numCopiedRows' : '999451335', 'numDeletionVectorsAdded' : '0', 'numDeletionVectorsRemoved' : '0', 'numAddedChangeFiles' : '0', 'executionTimeMs' : '123446', 'numDeletionVectorsUpdated' : '0', 'numDeletedRows' : '548665', 'scanTimeMs' : '4820', 'numAddedFiles' : '18', 'numAddedBytes' : '7696900084', 'rewriteTimeMs' : '198625'}
0 Fonctionnement ÉCRIRE
Paramètres d’opération {'mode' : 'Overwrite', 'partitionBy' : '[]'}
Métriques d’opération {'numFiles' : '8', 'numOutputRows' : '1000000000', 'numOutputBytes' : '7700892169'}

L’historique Delta Analyzer ci-dessus montre que cette table Delta comporte désormais les trois versions suivantes :

  • Version 0 : il s’agit de la version d’origine avec huit fichiers Parquet et 24 groupes de lignes, comme indiqué dans la section précédente.
  • Version 1 : cette version reflète l’opération de suppression . Bien qu’une seule journée de données (DateID = '20200101') ait été supprimée de l’exemple de table avec cinq ans de transactions de vente, les huit fichiers Parquet ont été affectés. Dans les métriques d’opération, numRemovedFiles est huit [fichiers Parquet] et numAddedFiles est 18 [fichiers Parquet]. Cela signifie que l’opération de suppression a remplacé les huit fichiers Parquet d’origine par 18 nouveaux fichiers Parquet.
  • Version 3 : Les métriques d’opération révèlent qu’un autre fichier Parquet avec 548 665 lignes a été ajouté à la table Delta.

Après la mise à jour de fenêtre propagée, la version de validation Delta la plus récente comprend 19 fichiers Parquet et 21 groupes de lignes, avec des tailles allant de 500 mille à 50 millions de lignes. La mise à jour par fenêtre glissante de 548 665 lignes a affecté la table Delta entière de 1 milliard de lignes. Il a remplacé tous les fichiers et groupes de lignes Parquet. L’encadrement incrémentiel ne peut pas être censé améliorer la performance à froid dans ce cas, et l'augmentation de la variation des tailles de groupe de lignes est peu susceptible d'améliorer la performance à chaud.

Mises à jour de la table Delta

Le code Python suivant met à jour une table Delta de la même façon que celle décrite dans la section précédente. Sur la surface, la fonction de mise à jour modifie uniquement la valeur DateID des lignes existantes qui correspondent à un DateID donné. Toutefois, les fichiers Parquet sont immuables et ne peuvent pas être modifiés. En arrière-plan, l’opération de mise à jour supprime les fichiers Parquet existants et ajoute de nouveaux fichiers Parquet. Le résultat et l’effet sont les mêmes que pour la mise à jour de fenêtre glissante.

from pyspark.sql.functions import col, when
from delta.tables import DeltaTable

# Load the Delta table
table_name = "<Provide your table name>"
delta_table = DeltaTable.forPath(spark, f"Tables/{table_name}")

# Define the condition and the column to update
condition = col("DateID") == 20200101
column_name = "DateID"
new_value = 20250101

# Update the DateID column based on the condition
delta_table.update(
    condition,
    {column_name: when(condition, new_value).otherwise(col(column_name))}
)

Mises à jour de fenêtres glissantes partitionnées

Le partitionnement peut aider à atténuer l'impact des mises à jour des tables. Il peut être tentant d’utiliser les clés de date, mais une vérification de cardinalité rapide peut révéler que ce n’est pas le meilleur choix. Par exemple, l’exemple de tableau abordé jusqu’à présent contient des transactions de ventes pour les cinq dernières années, ce qui équivaut à environ 1800 valeurs de date distinctes. Cette cardinalité est trop élevée. La colonne de partition doit avoir moins de 200 valeurs distinctes.

column_name = 'DateID'
table_name = "<Provide your table name>"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")

distinct_count = table_df.select(column_name).distinct().count()
print(f"The '{column_name}' column has {distinct_count} distinct values.")

if distinct_count <= 200:
    print(f"The '{column_name}' column is a good partitioning candidate.")
    table_df.write.format("delta").partitionBy(column_name).save(f"Tables/{table_name}_by_date_id")
    print(f"Table '{table_name}_by_date_id' partitioned and saved successfully.")
else:   
    print(f"The cardinality of the '{column_name}' column is possibly too high.")

Sortie :

The 'DateID' column has 1825 distinct values.
The cardinality of the 'DateID' column is possibly too high.

S’il n’existe aucune colonne de partition appropriée, elle peut être créée artificiellement en réduisant la cardinalité d’une colonne existante. Le code Python suivant ajoute une colonne Month en supprimant les deux derniers chiffres du DateID. Cela produit 60 valeurs distinctes. L’exemple de code enregistre ensuite la table Delta partitionnée par la colonne Month.

from pyspark.sql.functions import col, expr

column_name = 'DateID'
table_name = "sales_1"
table_df = spark.read.format("delta").load(f"Tables/{table_name}")

partition_column = 'Month'
partitioned_table = f"{table_name}_by_month"
table_df = table_df.withColumn(partition_column, expr(f"int({column_name} / 100)"))

distinct_count = table_df.select(partition_column).distinct().count()
print(f"The '{partition_column}' column has {distinct_count} distinct values.")

if distinct_count <= 200:
    print(f"The '{partition_column}' column is a good partitioning candidate.")
    table_df.write.format("delta").partitionBy(partition_column).save(f"Tables/{partitioned_table}")
    print(f"Table '{partitioned_table}' partitioned and saved successfully.")
else:   
    print(f"The cardinality of the '{partition_column}' column is possibly too high.")

Sortie :

The 'Month' column has 60 distinct values.
The 'Month' column is a good partitioning candidate.
Table 'sales_1_by_month' partitioned and saved successfully.

Le résumé Delta Analyzer montre maintenant que la disposition de la table Delta est bien alignée sur Direct Lake. La taille moyenne du groupe de lignes est d’environ 16 millions de lignes, et l’écart absolu moyen des tailles de groupe de lignes et par conséquent, les tailles de segment sont inférieures à 1 million de lignes.

Paramètre Valeur
Nom de la table ventes_1_par_mois
Nombre de lignes 1000000000
Groupes de lignes soixante
Fichiers Parquet soixante
Nombre maximal de lignes par groupe de lignes 16997436
Nombre minimal de lignes par groupe de lignes 15339311
Nombre moyen de lignes par groupe de lignes 16666666.666666666
VOrder activé Vrai
Taille totale 7447946016
Horodatage 2025-03-24 03:01:02.794979

Après une mise à jour en fenêtre glissante sur une table échantillon partitionnée, l’historique de Delta Analyzer indique qu’un seul fichier Parquet a été affecté. Consultez le tableau de sortie suivant. La version 2 comporte exactement 16 445 655 lignes copiées à partir de l’ancien fichier Parquet dans un fichier Parquet de remplacement, et la version 3 ajoute un nouveau fichier Parquet avec 548 665 lignes. Au total, Direct Lake doit uniquement recharger environ 17 millions de lignes, ce qui représente une amélioration considérable par rapport au rechargement de 1 milliard de lignes sans partitionnement.

Version Descriptif Valeur
2 Fonctionnement ÉCRIRE
Paramètres d’opération {'mode': 'Ajouter', 'partitionBy': '["Mois"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '548665', 'numOutputBytes' : '4103076'}
1 Fonctionnement Supprimer
Paramètres d’opération {'predicate' : '["(DateID#3910 = 20200101)"]'}
Métriques d’opération {'numRemovedFiles': '1', 'numRemovedBytes': '126464179', 'numCopiedRows': '16445655', 'numDeletionVectorsAdded': '0', 'numDeletionVectorsRemoved': '0', 'numAddedChangeFiles': '0', 'executionTimeMs': '19065', 'numDeletionVectorsUpdated': '0', 'numDeletedRows': '548665', 'scanTimeMs': '1926', 'numAddedFiles': '1', 'numAddedBytes': '121275513', 'rewriteTimeMs': '17138'}
0 Fonctionnement ÉCRIRE
Paramètres d’opération {'mode' : 'Overwrite', 'partitionBy' : '["Mois"]'}
Métriques d’opération {'numFiles' : '60', 'numOutputRows' : '1000000000', 'numOutputBytes' : '7447681467'}

Modèle en ajout seul suivi de l'optimisation par Spark

Les modèles d’ajout uniquement n’affectent pas les fichiers Parquet existants. Ils fonctionnent bien avec le framing incrémentiel de Direct Lake. Toutefois, n’oubliez pas d’optimiser vos tables Delta pour consolider les fichiers Parquet et les groupes de lignes, comme indiqué précédemment dans cet article. Les ajouts petits et fréquents peuvent accumuler rapidement des fichiers et peuvent fausser l’uniformité des tailles de groupe de lignes.

La sortie suivante montre l’historique Delta Analyzer d’une table nonpartitionnée par rapport à une table partitionnée. L’historique comprend sept ajouts et une opération d’optimisation ultérieure.

Version Descriptif Valeur dans la disposition par défaut Valeur dans la disposition partitionnée
8 Fonctionnement OPTIMISER OPTIMISER
Paramètres d’opération {'predicate' : '[]', 'auto' : 'false', 'clusterBy' : '[]', 'vorder' : 'true', 'zOrderBy' : '[]'} {'predicate' : '["('Month >= 202501)"]', 'auto' : 'false', 'clusterBy' : '[]', 'vorder' : 'true', 'zOrderBy' : '[]'}
Métriques d’opération {'numRemovedFiles' : '8', 'numRemovedBytes' : '991234561', 'p25FileSize' : '990694179', 'numDeletionVectorsRemoved' : '0', 'minFileSize' : '990694179', 'numAddedFiles' : '1', 'maxFileSize' : '990694179', 'p75FileSize' : '990694179', 'p50FileSize' : '990694179', 'numAddedBytes' : '990694179'} {'numRemovedFiles' : '7', 'numRemovedBytes' : '28658548', 'p25FileSize' : '28308495', 'numDeletionVectorsRemoved' : '0', 'minFileSize' : '28308495', 'numAddedFiles' : '1', 'maxFileSize' : '28308495', 'p75FileSize' : '28308495', 'p50FileSize' : '28308495', 'numAddedBytes' : '28308495'}
7 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionBy' : '["Mois"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '547453', 'numOutputBytes' : '4091802'} {'numFiles' : '1', 'numOutputRows' : '547453', 'numOutputBytes' : '4091802'}
6 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionBy' : '["Mois"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '548176', 'numOutputBytes' : '4095497'} {'numFiles' : '1', 'numOutputRows' : '548176', 'numOutputBytes' : '4095497'}
5 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionBy' : '["Mois"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '547952', 'numOutputBytes' : '4090107'} {'numFiles' : '1', 'numOutputRows' : '547952', 'numOutputBytes' : '4093015'}
4 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionnementPar' : '["Month"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '548631', 'numOutputBytes' : '4093134'} {'numFiles' : '1', 'numOutputRows' : '548631', 'numOutputBytes' : '4094376'}
3 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionBy' : '["Month"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '548671', 'numOutputBytes' : '4101221'} {'numFiles' : '1', 'numOutputRows' : '548671', 'numOutputBytes' : '4101221'}
2 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionBy' : '["Mois"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '546530', 'numOutputBytes' : '4081589'} {'numFiles' : '1', 'numOutputRows' : '546530', 'numOutputBytes' : '4081589'}
1 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Append', 'partitionBy' : '[]'} {'mode' : 'Append', 'partitionBy' : '["Month"]'}
Métriques d’opération {'numFiles' : '1', 'numOutputRows' : '548665', 'numOutputBytes' : '4101048'} {'numFiles' : '1', 'numOutputRows' : '548665', 'numOutputBytes' : '4101048'}
0 Fonctionnement ÉCRIRE ÉCRIRE
Paramètres d’opération {'mode' : 'Overwrite', 'partitionBy' : '[]'} {'mode' : 'Overwrite', 'partitionBy' : '["Mois"]'}
Métriques d’opération {'numFiles' : '8', 'numOutputRows' : '1000000000', 'numOutputBytes' : '7700855198'} {'numFiles' : '60', 'numOutputRows' : '1000000000', 'numOutputBytes' : '7447681467'}

En examinant les métriques d’opération de la version 8, il est important de souligner que l’opération d’optimisation de la table non partitionnée a fusionné huit fichiers Parquet affectant environ 1 Go de données, tandis que l’opération d’optimisation de la table partitionnée a fusionné sept fichiers Parquet affectant seulement environ 25 Mo de données. Il suit que Direct Lake fonctionne mieux avec la table partitionnée.

Considérations et limitations

Les considérations et limitations relatives à l’optimisation des performances de Direct Lake sont les suivantes :

  • Évitez les modèles de mise à jour destructeurs sur les grandes tables Delta pour conserver le cadre incrémentiel de Direct Lake.
  • Les petites tables Delta n’ont pas besoin d’être optimisées pour un formatage incrémentiel.
  • Visez une taille de groupe de lignes comprise entre 1 million et 16 millions de lignes pour créer des segments de colonnes dans Direct Lake, chacun contenant de 1 million à 16 millions de lignes. Direct Lake préfère les segments de colonnes volumineux.
  • Évitez les colonnes de partition de cardinalité élevée, car le transcodage Direct Lake est moins efficace avec de nombreux petits fichiers et groupes de lignes Parquet que avec moins de fichiers Parquet volumineux et de groupes de lignes.
  • En raison d’une demande imprévue de ressources de calcul et de mémoire, un modèle sémantique peut être rechargé sur un autre nœud de cluster Fabric dans un état froid.
  • Direct Lake n’utilise pas les statistiques delta\Parquet pour ignorer les groupes de lignes\fichiers afin d'optimiser le chargement des données.