Partager via


Résoudre les problèmes d’asymétrie des données dans Azure Data Lake Analytics à l’aide de Azure Data Lake Tools pour Visual Studio

Important

Azure Data Lake Analytics mis hors service le 29 février 2024. Découvrez-en plus avec cette annonce.

Pour l’analytique des données, votre organization peut utiliser Azure Synapse Analytics ou Microsoft Fabric.

Qu’est-ce que l'asymétrie des données ?

en résumé, l'asymétrie des données correspond à une valeur surreprésentée. Imaginez que vous avez affecté 50 examinateurs fiscaux pour vérifier les déclarations fiscales, un examinateur pour chaque État américain. L’examinateur du Wyoming, parce que la population y est petite, a peu à faire. En Californie, cependant, le contrôleur est occupé en raison de la forte population de cet état.

Exemple d’histogramme montrant la majorité des données regroupées en deux colonnes, au lieu d’être réparties uniformément entre les catégories.

Dans notre scénario, les données sont distribuées de manière inégale entre les contrôleurs fiscaux, ce qui signifie que certains d'entre eux doivent travailler plus que d’autres. Dans votre propre travail, vous rencontrez souvent des situations telles que celle-ci. En termes techniques, un vertex obtient beaucoup plus de données que ses pairs. Dans cette situation, le vertex doit travailler plus que les autres, ce qui ralentit toute la tâche. Qui plus est, la tâche peut échouer parce que les vertex sont, par exemple, limités à 5 heures d'exécution et à 6 Go de mémoire.

Résolution de problèmes liés à l'asymétrie des données

Azure Data Lake Tools pour Visual Studio et Visual Studio Code peuvent vous aider à détecter si votre travail présente un problème d’asymétrie des données.

En cas de problème, les solutions présentées dans cette section peuvent vous aider à les résoudre.

Solution 1 : Améliorer le partitionnement des tables

Option 1 : Filtrer la valeur de clé décalée à l’avance

Si cela n’affecte pas votre logique métier, vous pouvez filtrer les valeurs de fréquence supérieure à l’avance. Par exemple, s’il y a beaucoup de 000-000-000 dans le GUID de colonne, vous ne souhaiterez peut-être pas agréger cette valeur. Avant l’agrégation, vous pouvez écrire “WHERE GUID != “000-000-000”” pour filtrer la valeur la plus fréquente.

Option n°2 : Sélectionner une clé de distribution ou de partition différente

Dans l’exemple précédent, si vous souhaitez uniquement vérifier la charge de travail des contrôles fiscaux partout dans le pays/la région, vous pouvez améliorer la distribution des données en sélectionnant le numéro d’ID comme clé. La sélection d'une clé de distribution ou partition différente permet parfois de distribuer les données de façon plus uniforme, mais vous devez vous assurer que cela n’affecte pas votre logique métier. Par exemple, pour calculer la somme des taxes pour chaque état, vous pouvez désigner l'état comme clé de partition. Si vous continuez à rencontrer ce problème, essayez d’utiliser l’option 3.

Option 3 : Ajouter plus de clés de distribution ou de partition

Au lieu d’utiliser uniquement l'état comme clé de partition, vous pouvez utiliser plusieurs clés pour le partitionnement. Par exemple, envisagez d’ajouter le code postal comme autre clé de partition pour réduire les tailles de partition de données et distribuer les données de manière plus uniforme.

Option 4 : Utiliser la distribution par tourniquet (round robin)

Si vous ne trouvez pas de clé appropriée pour la partition et la distribution, vous pouvez essayer d’utiliser la distribution par tourniquet (round robin). La distribution par tourniquet (round robin) traite toutes les lignes de manière égale et les place au hasard dans des compartiments correspondants. Les données sont distribuées de façon égale, mais perdent les informations relatives à la localité, ce qui peut aussi réduire les performances de travail pour certaines opérations. En outre, si vous effectuez de toute façon l’agrégation pour la clé asymétrique, le problème d’asymétrie des données persiste. Pour en savoir plus sur la distribution par tourniquet (round robin), consultez la section Distributions de tables U-SQL dans CREATE TABLE (U-SQL) : Création d’une table avec un schéma.

Solution 2 : Améliorer le plan de requête

Option 1 : Utiliser l’instruction CREATE STATISTICS

U-SQL fournit l’instruction CREATE STATISTICS dans des tables. Cette instruction fournit plus d’informations à l’optimiseur de requête sur les caractéristiques des données (par exemple, la distribution de valeurs) qui sont stockées dans une table. Pour la plupart des requêtes, l’optimiseur de requête génère déjà les statistiques nécessaires pour un plan de requête de haute qualité. Parfois, vous devrez peut-être améliorer les performances des requêtes en créant davantage de statistiques avec CREATE STATISTICS ou en modifiant la conception de la requête. Pour plus d’informations, consultez la page CREATE STATISTICS (SQL-U).

Exemple de code :

CREATE STATISTICS IF NOT EXISTS stats_SampleTable_date ON SampleDB.dbo.SampleTable(date) WITH FULLSCAN;

Remarque

Les informations statistiques ne sont pas mises à jour automatiquement. Si vous mettez à jour les données dans une table sans recréer les statistiques, les performances des requêtes peuvent décliner.

Option n°2 : Utiliser SKEWFACTOR

Si vous souhaitez additionner les taxes pour chaque état, vous devez utiliser l'instruction GROUP BY, une approche qui n’évite pas le problème d'asymétrie des données. Toutefois, vous pouvez fournir un indicateur de données dans votre requête pour identifier l'asymétrie des données dans les clés, afin que l’optimiseur puisse préparer un plan d’exécution.

En règle générale, vous pouvez définir le paramètre sur 0,5 et 1, avec 0,5 qui signifie peu d’asymétrie et une asymétrie importante. L’indicateur affectant l’optimisation du plan d’exécution de l’instruction en cours et de toutes les instructions en aval, il est important d'ajouter l’indicateur avant l’agrégation permettant d’identifier le décalage éventuel dans les clés.

SKEWFACTOR (columns) = x

Fournit un indicateur indiquant que les colonnes données ont un facteur d’asymétrie x de 0 (aucune asymétrie) à 1 (asymétrie importante).

Exemple de code :

//Add a SKEWFACTOR hint.
@Impressions =
    SELECT * FROM
    searchDM.SML.PageView(@start, @end) AS PageView
    OPTION(SKEWFACTOR(Query)=0.5)
    ;
//Query 1 for key: Query, ClientId
@Sessions =
    SELECT
        ClientId,
        Query,
        SUM(PageClicks) AS Clicks
    FROM
        @Impressions
    GROUP BY
        Query, ClientId
    ;
//Query 2 for Key: Query
@Display =
    SELECT * FROM @Sessions
        INNER JOIN @Campaigns
            ON @Sessions.Query == @Campaigns.Query
    ;

Option 3 : Utiliser ROWCOUNT

Outre SKEWFACTOR, pour les cas de jonction de clés de décalage spécifiques, vous pouvez déterminer l’optimiseur en ajoutant un indicateur ROWCOUNT dans l’instruction U-SQL avant la jointure si vous savez que l’autre ensemble de lignes jointes est peu volumineux. De cette manière, l'optimiseur peut choisir une stratégie de diffusion conjointe pour améliorer les performances. N’oubliez pas que ROWCOUNT ne résout pas le problème d’asymétrie des données, mais qu’il peut offrir une aide supplémentaire.

OPTION(ROWCOUNT = n)

Identifiez un petit ensemble de lignes avant JOIN en fournissant une estimation du nombre de lignes (chiffre entier).

Exemple de code :

//Unstructured (24-hour daily log impressions)
@Huge   = EXTRACT ClientId int, ...
            FROM @"wasb://ads@wcentralus/2015/10/30/{*}.nif"
            ;
//Small subset (that is, ForgetMe opt out)
@Small  = SELECT * FROM @Huge
            WHERE Bing.ForgetMe(x,y,z)
            OPTION(ROWCOUNT=500)
            ;
//Result (not enough information to determine simple broadcast JOIN)
@Remove = SELECT * FROM Bing.Sessions
            INNER JOIN @Small ON Sessions.Client == @Small.Client
            ;

Solution 3 : Améliorer le réducteur et le combinateur définis par l’utilisateur

Parfois, vous écrivez l’opérateur défini par l’utilisateur pour gérer une logique de processus complexe, et un combinateur et un réducteur bien écrits peuvent, dans certains cas, réduire les problèmes d'asymétrie des données.

Option 1 : Utiliser un réducteur récursif si possible

Par défaut, un réducteur défini par l’utilisateur s’exécute en mode non récursif, ce qui signifie que réduire le travail pour une clé est distribué dans un seul sommet. Mais, si vos données sont asymétriques, les ensembles de données volumineux peuvent être traités dans un seul vertex et cette exécution peut prendre un certain temps.

Pour améliorer les performances, vous pouvez ajouter un attribut dans votre code pour définir une exécution du réducteur en mode récursif. Ensuite, les grands ensembles de données peuvent être distribués sur plusieurs vertex et s’exécuter en parallèle, ce qui accélère votre travail.

Pour remplacer un réducteur non récursif par récursif, vous devez vous assurer que votre algorithme est associatif. Par exemple, la somme est associative, et la médiane ne l’est pas. Vous devez également vous assurer que l’entrée et la sortie du réducteur conservent le même schéma.

Attribut d'un réducteur récursif :

[SqlUserDefinedReducer(IsRecursive = true)]

Exemple de code :

[SqlUserDefinedReducer(IsRecursive = true)]
public class TopNReducer : IReducer
{
    public override IEnumerable<IRow>
        Reduce(IRowset input, IUpdatableRow output)
    {
        //Your reducer code goes here.
    }
}

Option n°2 : Utiliser le mode de combinateur au niveau des lignes si possible

À l’instar de l’indicateur ROWCOUNT pour les cas de jonction de clés de décalage spécifiques, le mode de combinateur essaie de distribuer les ensembles de valeurs de clés de décalage dans plusieurs vertex pour que le travail puisse être exécuté simultanément. Le mode combinateur ne peut pas résoudre les problèmes d’asymétrie des données, mais il peut offrir une aide supplémentaire pour d’énormes jeux de valeurs de clé asymétrique.

Par défaut, le mode combinateur est Complet, ce qui signifie que l’ensemble de lignes de gauche et l’ensemble de lignes de droite ne peuvent pas être séparés. La définition du mode sur gauche/droite/interne permet une jointure au niveau des lignes. Le système sépare les ensembles de lignes correspondants et les répartit dans plusieurs vertex qui s’exécutent en parallèle. Toutefois, avant de configurer le mode de combinateur, assurez-vous que les ensembles de lignes correspondants peuvent être séparés.

L’exemple qui suit montre un ensemble de lignes gauche distinct. Chaque ligne de sortie dépend d’une seule ligne d’entrée de gauche, et potentiellement de toutes les lignes de droite avec la même valeur de clé. Si vous définissez le mode de combinateur sur gauche, le système sépare le grand ensemble de lignes de gauche en plusieurs petits ensembles et les distribue dans plusieurs vertex.

Deux colonnes de lignes représentant des jeux de données de gauche et de droite, montrant certaines lignes du jeu de données de droite déplacées dans le premier groupe du jeu de données de gauche.

Remarque

Si vous définissez le mode de combinateur incorrect, la combinaison est moins efficace, et les résultats peuvent être erronés.

Attributs du mode de combinateur :

  • SqlUserDefinedCombiner(Mode=CombinerMode.Full) : Chaque ligne de sortie dépend potentiellement de toutes les lignes d’entrée de gauche et de droite avec la même valeur de clé.

  • SqlUserDefinedCombiner(Mode=CombinerMode.Left) : chaque ligne de sortie dépend d’une seule ligne d’entrée de gauche (et potentiellement de toutes les lignes de droite avec la même valeur de clé).

  • qlUserDefinedCombiner(Mode=CombinerMode.Right) : chaque ligne de sortie dépend d’une seule ligne d’entrée de droite (et potentiellement de toutes les lignes de gauche avec la même valeur de clé).

  • SqlUserDefinedCombiner(Mode=CombinerMode.Inner) : chaque ligne de sortie dépend d’une seule ligne d’entrée de gauche et droite avec la même valeur.

Exemple de code :

[SqlUserDefinedCombiner(Mode = CombinerMode.Right)]
public class WatsonDedupCombiner : ICombiner
{
    public override IEnumerable<IRow>
        Combine(IRowset left, IRowset right, IUpdatableRow output)
    {
    //Your combiner code goes here.
    }
}