Connect(); 2016

Volume 31, numéro 12

Cet article a fait l'objet d'une traduction automatique.

Connect(); Application intelligentes - Extensibilité dans les applications Big Data U-SQL

Par Rys de de ; 2016

L’objectif traditionnel sur l’adressage le V volumineux de données volumineuses, volume, rapidité et la diversité — pendant les données volumineuses traitement a concentré essentiellement sur fournissant une plateforme évolutive pour traiter le volume de données, sur l’ajout de capacités de traitement de quasiment en temps réel et offre la possibilité de traiter divers formats de données d’entrée, de CSV sur JSON à formats binaires personnalisés. Une variété a souvent été en quelque sorte une priorité concerne la variété associée au traitement des données personnalisées, non seulement en termes de format, mais également la possibilité de rendre facile d’étendre votre analyse avec les algorithmes personnalisés tout en conservant la nature déclarative de l’expérience de langage de requête.

Certains langages de requête et de traitement des données volumineuses modernes commencent à résoudre ce problème. En particulier, U-SQL a été conçu dès le départ pour associer la puissance déclarative d’un langage SQL et la flexibilité de l’utilisation de vos bibliothèques de code existant et le développement de nouveaux algorithmes personnalisés.

Dans un article précédent (bit.ly/1OtXM2K), j’introduit U-SQL et vous a montré comment Microsoft .NET Framework à l’aide de système de type avec c#-langage d’expression en fonction U-SQL rend transparente pour étendre votre analyse des expressions de code personnalisé. J’ai expliqué comment utiliser les assemblys c# pour définir des fonctions définies par l’utilisateur (UDF) et de les utiliser dans vos scripts de requête SQL-U.

U-SQL vous permet non seulement ajouter vos propres fonctions personnalisées de c#, mais il fournit également une infrastructure dans laquelle vous pouvez ajouter vos propres opérateurs définis par l’utilisateur (opérateurs), telles que vos propres extracteurs, outputters et opérateurs d’ensemble de lignes, tels que les processeurs, appliers, réducteurs et combinateurs personnalisés. Elle est constituée de deux parties :

  1. Interfaces .NET qui fournissent le contrat pour générer ces opérateurs de sorte que vous pouvez vous concentrer sur votre code, en laissant l’exécution de la montée en charge U-SQL. Notez que le code de logique métier réels ne doit être implémentées dans .NET, comme je le démontrerai plus tard.
  2. Expressions de U-SQL telles qu’EXTRAIT et RÉDUCTION qui appellent des opérateurs personnalisés et de les exécuter à grande échelle sur vos données.

Dans cet article, je générer dans l’article précédent et montrent comment vous pouvez utiliser les mécanismes d’extensibilité U-SQL pour traiter une variété de données différents, allant de JSON pour les données d’image. Je montrerai également comment ajouter vos propres opérateurs.

La gestion de votre Code personnalisé en U-SQL

Avant de commencer avec certains des exemples, nous allons mieux comprendre comment U-SQL peut utiliser du code personnalisé.

Comme mentionné, U-SQL suit c# avec son langage d’expression scalaire, qui est utilisé dans des lieux comme prédicats U-SQL et les expressions dans une clause select. Votre code personnalisé soit visible pour le compilateur U-SQL, le code doit être empaqueté dans un assembly .NET qui doit être référencé par le script SQL-U. Pour pouvoir référencer l’assembly, il doit avoir préalablement enregistrée dans le service de métadonnées U-SQL à l’aide d’une instruction CREATE ASSEMBLY.

Enregistrement et référencement des assemblys SQL-U je suggère d’utiliser les outils de LAC de données Azure pour Visual Studio (aka.ms/adltoolsvs), qui permet de générer et inscrire des assemblys qui fonctionnent avec SQL-U. Si vous écrivez votre code personnalisé dans un projet de bibliothèque de classes » (pour Application U-SQL) » (voir Figure 1), vous pouvez écrire votre code et générer le projet et inscrire directement le fichier DLL d’assembly généré avec un clic droit (voir Figure 2).

Projet de bibliothèque (pour les applications SQL-U) de classe
Projet de bibliothèque (pour les applications SQL-U) figure 1 classe

Enregistrement d’un Assembly U-SQL
Figure 2 enregistrement d’un Assembly U-SQL

Il vous suffit dans votre Script SQL-U est l’instruction d’ASSEMBLY de RÉFÉRENCE pour rendre les classes publiques et les méthodes utilisables dans votre script SQL-U, comme indiqué dans Figure 3.

Figure 3 faisant référence à une fonction définie par l’utilisateur à partir d’un Assembly personnalisé

REFERENCE ASSEMBLY master.TweetAnalysis;
USING tweet_fns = TweetAnalysis.Udfs;
@t =
  EXTRACT date string,
          time string,
          author string,
          tweet string
  FROM "/Samples/Data/Tweets/Tweets.csv"
  USING Extractors.Csv();
// Get the mentions from the tweet string
@m =
  SELECT origin
       , tweet_fns.get_mentions(tweet) AS mentions
       , author AS mentioned_by
FROM @t;
...

À l’aide du Code existant avec les assemblys SQL-U souvent que vous souhaitez utiliser les bibliothèques de code existant ou même non .NET code. Si vous souhaitez utiliser le code non .NET, par exemple, une bibliothèque native ou même un runtime de langage complètement différent comme Python ou JavaScript, vous devez encapsuler le code non .NET avec une couche d’interopérabilité c# qui sera appelé à partir d’U-SQL et qui appelle ensuite le code non .NET, marshaling de données entre les composants et l’implémentation d’un contrat d’interface UDO. Dans ce cas, les artefacts de code non .NET tels que les DLL natives ou les fichiers de l’exécution de différentes doivent être ajoutés en tant que fichiers supplémentaires. Pour cela, dans l’option fichier supplémentaire de l’inscription de l’assembly. Ces fichiers sont déployés automatiquement à tous les nœuds lors de l’assembly .NET est référencée dans un script et est disponible pour le répertoire de travail de l’assembly .NET localement à ce nœud.

Pour utiliser les bibliothèques .NET existantes, vous devez enregistrer les bibliothèques de code existant en tant que dépendances gérés sur votre propre assembly, ou, si vous réutilisez une bibliothèque que vous pouvez utiliser directement dans U-SQL, l’enregistrer directement dans votre base de données SQL-U. Dans les deux cas, le script doit référencer tous les assemblys .NET requis par le script.

Je vais montrer quelques exemples de ces options d’enregistrement dans le reste de l’article que j’aborde certains scénarios de code personnalisé dans lequel il est pratique d’utiliser le modèle d’extensibilité. Ces scénarios comprennent : fusionner des plages qui se chevauchent avec un raccord de réduction personnalisé, le traitement des documents JSON, traitement des données d’image et traitement des données spatiales. J’aborderai chacun à son tour.

Fusionner des plages qui se chevauchent avec un raccord de réduction personnalisée

Supposons que vous avez un fichier journal qui effectue le suivi lorsque l’utilisateur interagit avec votre service. En outre, supposons qu’un utilisateur peut interagir avec votre service de plusieurs façons (par exemple, en recherches Bing depuis plusieurs périphériques ou les fenêtres du navigateur). Dans le cadre de votre travail U-SQL qui prépare le fichier journal pour une analyse ultérieure, que vous souhaitez fusionner des plages qui se chevauchent.

Par exemple, si le fichier journal se présente comme Figure 4, puis à fusionner les plages qui se chevauchent pour chaque utilisateur dans Figure 5.

Fichier journal de la figure 4 avec chevauchement des intervalles de temps

Heure de début  Heure de fin  Nom de l'utilisateur
5 H 00  6:00 AM  ABC
5 H 00  6:00 AM  XYZ
8 H 00  9 H 00  ABC
8 H 00  10 H 00  ABC
10 H 00  2 H 00  ABC
7 H 00  11:00 PST  ABC
9 H 00  11:00 PST  ABC
11:00 PST  11:30 PST  ABC
11:40 PM  11:59 PM  FOO
11:50 PM  0:40 H  FOO

Figure 5 du journal après la fusion de chevauchement des intervalles de temps

Heure de début  Heure de fin  Nom de l'utilisateur
5 H 00  6:00 AM  ABC
5 H 00  6:00 AM  XYZ
7 H 00  2 H 00  ABC
11:40 PM  0:40 H  FOO

Si vous examinez le problème, vous remarquerez tout d’abord que vous souhaitez définir quelque chose comme une agrégation définie par l’utilisateur de combiner les intervalles de temps qui se chevauchent. Toutefois, si vous examinez les données d’entrée, vous remarquerez que parce que les données n’est pas ordonnées, soit avoir à maintenir l’état de tous les intervalles possibles, puis fusionnez intervalles disjoints comme intervalles pontage s’affichent, ou vous devez preorder les intervalles pour chaque nom d’utilisateur effectuer la fusion des intervalles plus facilités.

L’agrégation ordonnée est plus simple de montée en puissance parallèle, mais U-SQL ne fournit pas ordonnés agrégateurs définis par l’utilisateur (UDAGGs). En outre, UDAGGs produisent normalement une ligne par groupe, alors que dans ce cas, je peux avoir plusieurs lignes par groupe si les plages sont les plages disjointes.

Heureusement, U-SQL fournit un OPÉRATEUR évolutive appelée un raccord de réduction (bit.ly/2evGsDA) qui peut regrouper un ensemble de lignes basé sur un jeu de clés de regroupement à l’aide de code personnalisé.

Nous allons préalablement à écrire la logique de U-SQL où ReduceSample.RangeReducer est notre réduction définis par l’utilisateur (UDO raccord de réduction) à partir de l’assembly RangeReducer, et les données du journal se trouve dans le fichier /Samples/Blogs/MRys/Ranges/ranges.txt (bit.ly/2eseZyw) et utilise «- » comme délimiteur de colonne. Voici le code :

REFERENCE ASSEMBLY RangeReducer;
@in = EXTRACT start DateTime, end DateTime, user string
FROM "/Samples/Blogs/MRys/Ranges/ranges.txt"
USING Extractors.Text(delimiter:'-');
@r =  REDUCE @in PRESORT start ON user
      PRODUCE start DateTime, end DateTime, user string
      READONLY user
      USING new ReduceSample.RangeReducer();
OUTPUT @r
TO "/temp/result.csv"
USING Outputters.Csv();

L’expression REDUCE prend l’ensemble de lignes @ dans presorts les partitions basées sur les valeurs de la colonne début comme entrée, il est basé sur la colonne de l’utilisateur, les partitions et s’applique le RangeReducer, produire le même schéma d’ensemble de lignes sur la sortie. Étant donné que le raccord de réduction ajuste uniquement la plage de début et la fin, il ne réellement touche la colonne de l’utilisateur, donc vous marquez comme étant en lecture SEULE. Cela autorise l’infrastructure de raccord de réduction pour transférer les données automatiquement pour cette colonne et, en retour, permet le processeur de requêtes SQL-U à appliquer efficacement des optimisations autour des colonnes en lecture seule, comme aux prédicats push sur une colonne en lecture seule avant le raccord de réduction.

La méthode pour écrire un raccord de réduction consiste à implémenter une instance de Microsoft.Analytics.Interfaces.IReducer. Dans ce cas, étant donné que vous n’avez pas besoin de fournir des paramètres, vous devez uniquement remplacer la méthode abstraite de réduction. Vous pouvez copier le code dans une bibliothèque de code c# pour U-SQL et l’enregistrer en tant que l’assembly RangeReducer comme expliqué précédemment. Figure 6 illustre l’implémentation de la RangeReducer. (Notez que les pratiques de mise en retrait normale du code ont été modifiés dans des exemples de code en raison des contraintes d’espace).

Figure 6 C# implémentation de la RangeReducer

using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ReduceSample
{
  public class RangeReducer : IReducer
  {
    public override IEnumerable<IRow> Reduce(
      IRowset input, IUpdatableRow output)
    {
      // Init aggregation values
      bool first_row_processed = false;
      var begin = DateTime.MaxValue;
      var end = DateTime.MinValue;
      // Requires that the reducer is PRESORTED on begin and
      // READONLY on the reduce key.
      foreach (var row in input.Rows)
      {
        // Initialize the first interval with the first row if i is 0
       if (!first_row_processed)
        {
         first_row_processed = true; // Mark that the first row was handled
          begin = row.Get<DateTime>("start");
          end = row.Get<DateTime>("end");
          // If the end is just a time and not a date, it can be earlier
          // than the begin, indicating it is on the next day;
          // this let's you fix up the end to the next day in that case
          if (end < begin) { end = end.AddDays(1); }
        }
        else // Handle the remaining rows
        {
          var b = row.Get<DateTime>("start");
          var e = row.Get<DateTime>("end");
          // Fix up the date if end is earlier than begin
          if (e < b) { e = e.AddDays(1); }
          // If begin time is still inside the interval,
          // increase the interval if it is longer
          if (b <= end)
          {
            // If the new end time is later than the current,
            // extend the interval
            if (e > end) { end = e; }
          }
          else // Output the previous interval and start a new one
          {
            output.Set<DateTime>("start", begin);
            output.Set<DateTime>("end", end);
            yield return output.AsReadOnly();
            begin = b; end = e;
          } // if
        } // if
      } // foreach
      // Now output the last interval
      output.Set<DateTime>("start", begin);
      output.Set<DateTime>("end", end);
      yield return output.AsReadOnly();
    } // Reduce
  } // RangeReducer
} // ReduceSample

L’expression RÉDUIRE U-SQL s’applique à la méthode réduire une fois pour chaque clé de partition distincts en parallèle. Le paramètre d’entrée ainsi contiendra uniquement les lignes pour un groupe donné et l’implémentation peut retourner zéro à plusieurs lignes en tant que sortie.

Car la clause PRESORT garantit que les lignes sont triées, que la logique interne peut assumer que les données sont triées, et la colonne de l’utilisateur est marquée en lecture SEULE, la colonne sera automatiquement transmise et vous pouvez écrire votre code de l’OPÉRATEUR plus génériquement en se concentrant uniquement sur les colonnes que vous souhaitez transformer.

Si vous appliquez maintenant le raccord de réduction sur un grand nombre de données, et si certains de vos utilisateurs peuvent utiliser votre système beaucoup plus fréquemment que d’autres personnes, vous rencontrerez ce que l'on appelle des données du décalage où certains utilisateurs ont des grandes partitions et autres petites partitions uniquement. Étant donné que le contrat du raccord de réduction est la garantie de voir toutes les données de cette partition, toutes les données doivent être mélangées à ce nœud et lire dans un seul appel. Cette exigence, ces données dans le meilleur des cas peuvent entraîner certaines partitions prendre beaucoup plus de temps à traiter que d’autres et dans le pire des cas peuvent entraîner des réducteurs insuffisant pour la mémoire disponible et les ressources de temps (un sommet U-SQL expire après l’exécution de cinq heures environ).

Si la sémantique de raccord de réduction est associatives et commutatives et son schéma de sortie est identique à celle de son schéma d’entrée, puis un raccord de réduction peut être marquée comme récursive, ce qui permet au moteur de requête à fractionner les grands groupes plus petits groupes et des sous-groupes de manière récursive s’appliquent le raccord de réduction sur ces sous-groupes pour calculer le résultat final. Cette application récursive permet de mieux équilibrer le raccord de réduction et paralléliser en présence de décalage de données. Un raccord de réduction est marqué comme récursive à l’aide de l’annotation de la propriété SqlUserDefinedReducer(IsRecursive = true) :

namespace ReduceSample
{
  [SqlUserDefinedReducer(IsRecursive = true)]
  public class RangeReducer : IReducer
  {
    public override IEnumerable<IRow> Reduce(
      IRowset input, IUpdatableRow output)
    {
      // Insert the code from Figure 6 here
    } // Reduce
  } // RangeReducer
} // ReduceSample

Dans notre cas, le raccord de réduction peut être marquée comme récursive pour améliorer l’évolutivité et les performances, en supposant que le traitement permet de conserver le tri entre les lignes dans chaque appel récursif.

Vous trouverez un projet Visual Studio pour l’exemple sur notre référentiel GitHub à bit.ly/2ecLe5B.

Traitement des Documents JSON

Un des formats de données plus fréquemment après des fichiers texte séparés par des virgules est JSON. Contrairement aux formats de fichier CSV, U-SQL ne fournit pas un extracteur de JSON intégré. Toutefois, la Communauté U-SQL a fourni un exemple d’assembly à bit.ly/2d9O4va qui offre la prise en charge pour l’extraction et traitement des documents JSON et XML.

Cette solution utilise une bibliothèque de Json.NET de Newtonsoft (bit.ly/2evWJbz) des lourdes JSON levage System.XML pour le traitement XML. L’assembly peut extraire des données à partir d’un document JSON à l’aide de la JsonExtractor (bit.ly/2dPARsM), prendre un document JSON et diviser en un SqlMap pour permettre la navigation et la décomposition des documents JSON avec la fonction JsonTuple (bit.ly/2e8tSuX) et enfin transformer un ensemble de lignes dans un fichier au format JSON avec la JSONOutputter (bit.ly/2e4uv3W).

Notez que l’assembly est conçu pour être un processeur JSON générique, ce qui signifie qu’il ne fait pas d’hypothèse concernant la structure du document JSON et doit être résilient à la nature de JSON, y compris hétérogène semi-structurées typé éléments (scalaires par rapport aux types de données structurées, différentes pour le même élément, manque des éléments et ainsi de suite). Si vous connaissez que vos documents JSON adhèrent à un schéma spécifique, vous pouvez éventuellement créer un extracteur de JSON plus efficace.

Contrairement à dans l’exemple de raccord de réduction plus haut, lorsque vous écrivez votre propre assembly que vous déployez puis, dans ce cas la solution est prête à être utilisée. Vous pouvez charger la solution à partir de notre référentiel GitHub dans Visual Studio et générer et déployer vous-même, ou vous pouvez rechercher les DLL dans le répertoire bin\Debug de la solution.

Comme mentionné précédemment, la dépendance non système nécessite que le Samples.Format et les assemblys Json.NET doivent être enregistrés dans le magasin de métadonnées U-SQL (vous pouvez sélectionner l’assembly Newtonsoft en tant que dépendance géré lors de l’inscription de l’assembly de Format à l’aide de l’outil Visual Studio) et les deux doivent être référencées si vous souhaitez traiter des documents JSON. Si vous avez installé vos assemblys JSON dans votre catalogue U-SQL sous le nom [Microsoft.Analytics.Samples.Formats] et [NewtonSoft.Json] dans la base de données SQL-U JSONBlog (voir Figure 7), vous pouvez utiliser les assemblys en référençant les au début de vos scripts :

REFERENCE ASSEMBLY JSONBlog.[NewtonSoft.Json];
REFERENCE ASSEMBLY JSONBlog.[Microsoft.Analytics.Samples.Formats];

Enregistrement de l’Assembly de Formats dans Visual Studio
Figure 7 l’inscription de l’Assembly de Formats dans Visual Studio

L’extracteur JSON implémente l’interface de U-SQL IExtractor. Étant donné que les documents JSON doivent être entièrement analysé pour vérifier si qu'elles sont bien formés, un fichier contenant un seul document JSON devrez traiter dans un seul sommet extracteur. Par conséquent, vous indiquez que l’extracteur doit voir le contenu complet du fichier en définissant la propriété AtomicFileProcessing sur true (voir Figure 8). L’extracteur de peut être appelée avec un paramètre optionnel appelé rowpath qui permet d’identifier les objets JSON qui chacun mappés à une ligne à l’aide d’une expression JSONPath (bit.ly/1EmvgKO).

Figure 8 l’extracteur JSON

[SqlUserDefinedExtractor(AtomicFileProcessing = true)]
public class JsonExtractor : IExtractor
{
  private string rowpath;            
  public JsonExtractor(string rowpath = null)
  {
    this.rowpath = rowpath;
  }
  public override IEnumerable<IRow> Extract(
    IUnstructuredReader input, IUpdatableRow output)
  {
    // Json.NET
    using (var reader = new JsonTextReader(
      new StreamReader(input.BaseStream)))
    {
      // Parse Json
      var root = JToken.ReadFrom(reader);
      // Rows
      // All objects are represented as rows
      foreach (JObject o in SelectChildren(root, this.rowpath))
      {
        // All fields are represented as columns
        this.JObjectToRow(o, output);
        yield return output.AsReadOnly();
      }
    }
  }
}

L’implémentation d’extracteur transmettra le flux d’entrée de flux de l’infrastructure de l’extracteur de U-SQL dans l’extracteur d’à la Json.NET JsonTextReader. Ensuite, il utilisera le rowpath pour obtenir les sous-arborescences qui est mappés à une ligne à l’aide de SelectChildren. Étant donné que les objets JSON peuvent être hétérogènes, le code retourne le JObject générique au lieu de JArray positionnel ou des valeurs scalaires.

Notez que cela cet extracteur de chargement du document JSON en mémoire. Si votre document est trop volumineux, il pourrait provoquer une condition de mémoire insuffisante. Dans ce cas, vous devrez écrire votre propre extracteur de ce flux de données dans le document sans avoir à charger le document entier en mémoire.

Maintenant nous allons utiliser l’extracteur de JSON et la fonction de tuple JSON à analyser le document JSON complex à partir de /Samples/Blogs/MRys/JSON/complex.json (bit.ly/2ekwOEQ) fourni dans Figure 9.

Exemple de Document figure 9 JSON

[{
  "person": {
    "personid": 123456,
    "name": "Person 1",
    "addresses": {
      "address": [{
        "addressid": "2",
        "street": "Street 2",
        "postcode": "1234 AB",
        "city": "City 1"
      }, {
        "addressid": "2",
        "street": "Street 2",
        "postcode": "5678 CD",
        "city": "City 2"
      }]
    }
  }
}, {
     "person": {
     "personid": 798,
     "name": "Person 2",
     "addresses": {
       "address": [{
         "addressid": "1",
         "street": "Street 1",
         "postcode": "1234 AB",
         "city": "City 1"
     }, {
         "addressid": "4",
         "street": "Street 7",
         "postcode": "98799",
         "city": "City 3"
     }]
   }
  }
}]

Le format est un tableau de personne « objets » (techniquement avec une clé de personne) qui à son tour contenir certaines propriétés de la personne et traiter des objets. Le script SQL-U de Figure 10 extrait une ligne pour chaque combinaison de personne/adresse.

Figure 10 U-SQL Script traitement de l’exemple de Document JSON à partir de la Figure 9

DECLARE @input string = "/Samples/Blogs/MRys/JSON/complex.json";
REFERENCE ASSEMBLY JSONBlog.[Newtonsoft.Json];
REFERENCE ASSEMBLY JSONBlog.[Microsoft.Analytics.Samples.Formats];
USING Microsoft.Analytics.Samples.Formats.Json;
@json =
  EXTRACT personid int,
          name string,
          addresses string
  FROM @input
  USING new JsonExtractor("[*].person");
@person =
  SELECT personid,
         name,
         JsonFunctions.JsonTuple(
           addresses, "address")["address"] AS address_array
  FROM @json;
@addresses =
  SELECT personid,
         name,
         JsonFunctions.JsonTuple(address) AS address
  FROM @person
       CROSS APPLY
         EXPLODE (JsonFunctions.JsonTuple(address_array).Values)
           AS A(address);
@result =
  SELECT personid,
         name,
         address["addressid"]AS addressid,
         address["street"]AS street,
         address["postcode"]AS postcode,
         address["city"]AS city
  FROM @addresses;
OUTPUT @result
TO "/output/json/persons.csv"
USING Outputters.Csv();

Notez que le script transmet la .person expression [*] JSONPath à l’extracteur, générant ainsi une ligne pour chaque champ person dans le tableau de niveau supérieur. L’EXTRAIT de schéma est utilisé par l’extracteur pour obtenir les propriétés de l’objet résultant dans des colonnes. Étant donné que le champ adresse est lui-même un document JSON imbriqué, le premier appel de la fonction JsonTuple crée une table contenant les objets d’adresse, qui sont ensuite mappés à une seule ligne par adresse avec l’expression Cross-APPLIQUER ÉCLATER. Enfin, toutes les propriétés d’adresse sont projetées à partir des données de carte tapant pour vous donner l’ensemble de lignes, comme indiqué dans Figure 11.

Figure 11 l’ensemble de lignes généré par le traitement du Document JSON à partir de la Figure 9

123456 Personne 1 2 Rue 2 1234 AB Ville 1
123456 Personne 1 2 Rue 2 CD 5678 Ville 2
798 Personne 2 1 Rue 1 1234 AB Ville 1
798 Personne 2 4 Rue 7 98799 City 3

Vous trouverez un projet Visual Studio de l’exemple et d’autres scénarios de traitement JSON, y compris plusieurs documents JSON à l’intérieur d’un fichier, dans notre référentiel GitHub à bit.ly/2dzceLv.

Traitement des données Image

Dans cet exemple, je suis de traitement des données non structurées supérieure : les images. En particulier, je souhaite traiter les images JPEG et extraire certaines propriétés JPEG EXIF, ainsi que pour créer une miniature de l’image. Heureusement, .NET fournit une variété de fonctions dans la classe System.Drawing de traitement d’image. Donc je dois faire est build U-SQL des fonctions d’extension et les opérateurs, déléguer le traitement à ces classes d’image JPEG.

Il y a plusieurs façons de procéder. Une tentative initiale peut charger toutes les images en tant que tableaux d’octets dans un ensemble de lignes, puis appliquez des fonctions individuelles définies par l’utilisateur pour extraire chacune des propriétés et créer la miniature, comme indiqué dans Figure 12.

Figure 12 Images U-SQL de traitement par le chargement des Images dans les lignes

REFERENCE ASSEMBLY Images;
USING Images;
@image_data =
  EXTRACT image_data byte[]  // Max size of row is 4MB!
        , name string
        , format string
  FROM @"/Samples/Data/Images/{name}.{format}"
  USING new ImageExtractor();
// Use UDFs
@image_properties =
  SELECT ImageOps.getImageProperty(image_data, ImageProperties.copyright)
         AS image_copyright,
         ImageOps.getImageProperty(image_data, ImageProperties.equipment_make)
         AS image_equipment_make,
         ImageOps.getImageProperty(image_data, ImageProperties.equipment_model)
         AS image_equipment_model,
         ImageOps.getImageProperty(image_data, ImageProperties.description)
         AS image_description
  FROM @image_data
  WHERE format IN ("JPEG", "jpeg", "jpg", "JPG");

Toutefois, cette approche présente certains inconvénients :

  • Les lignes U-SQL ne doivent pas dépasser 4 Mo la taille, ce qui limite la solution aux images de taille de 4 Mo (moins la taille des autres colonnes).
  • Chacun des appels de la fonction peut ajouter à la pression de mémoire et requiert transitant via le traitement U-SQL, le tableau d’octets.

Par conséquent, une meilleure approche consiste à effectuer la création de propriété d’extraction et de miniature directement à l’intérieur de l’extracteur personnalisé. Figure 13 montre un script SQL-U révisé.

Traitement d’Images en U-SQL en extrayant les fonctionnalités avec un extracteur de la figure 13

REFERENCE ASSEMBLY Images;
@image_features =
  EXTRACT copyright string,
          equipment_make string,
          equipment_model string,
          description string,
          thumbnail byte[],
          name string,
          format string
  FROM @"/Samples/Data/Images/{name}.{format}"
  USING new Images.ImageFeatureExtractor(scaleWidth:500, scaleHeight:300);
@image_features =
  SELECT *
  FROM @image_features
  WHERE format IN ("JPEG", "jpeg", "jpg", "JPG");
OUTPUT @image_features
TO @"/output/images/image_features.csv"
USING Outputters.Csv();
@scaled_image =
  SELECT thumbnail
  FROM @image_features
  WHERE name == "GT4";
OUTPUT @scaled_image
TO "/output/images/GT4_thumbnail_2.jpg"
USING new Images.ImageOutputter();

Ce script extrait les propriétés et la miniature les images spécifiées par le modèle de jeu de fichiers (bit.ly/2ektTY6) : exemples/Data/Images / {nom}. {} format}. L’instruction SELECT puis limite l’extraction de fichiers JPEG en utilisant un prédicat dans la colonne de format qui supprime tous les fichiers non-JPEG à partir de l’extraction (l’optimiseur s’applique uniquement l’extracteur pour les fichiers qui répondent au prédicat sur la colonne format). L’extracteur offre la possibilité de spécifier les dimensions de la miniature. Le script génère les fonctionnalités dans un fichier CSV, puis utilise une simple outputter niveau octet-stream pour créer un fichier de miniature pour l’une des images réduite.

Figure 14 illustre l’implémentation de l’extracteur.

Figure 14 l’extracteur d’Image

using Microsoft.Analytics.Interfaces;
using Microsoft.Analytics.Types.Sql;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
namespace Images
{
  public static class UpdatableRowExtensions
  {
    public static void SetColumnIfExists<T>(this IUpdatableRow source
                                           , string colName, T value)
    {
      var colIdx = source.Schema.IndexOf(colName);
      if (colIdx != -1)
      { source.Set<T>(colIdx, value); }
    }
  }
  [SqlUserDefinedExtractor(AtomicFileProcessing = true)]
  public class ImageFeatureExtractor : IExtractor
  {
    private int _scaleWidth, _scaleHeight;
    public ImageFeatureExtractor(int scaleWidth = 150, int scaleHeight = 150)
    { _scaleWidth = scaleWidth; _scaleHeight = scaleHeight; }
    public override IEnumerable<IRow> Extract(IUnstructuredReader input
                                             , IUpdatableRow output)
    {
      byte[] img = ImageOps.GetByteArrayforImage(input.BaseStream);
      using (StreamImage inImage = new StreamImage(img))
      {
        output.SetColumnIfExists("image", img);
        output.SetColumnIfExists("equipment_make",
          inImage.getStreamImageProperty(ImageProperties.equipment_make));
        output.SetColumnIfExists("equipment_model",
          inImage.getStreamImageProperty(ImageProperties.equipment_model));
        output.SetColumnIfExists("description",
          inImage.getStreamImageProperty(ImageProperties.description));
        output.SetColumnIfExists("copyright",
          inImage.getStreamImageProperty(ImageProperties.copyright));
        output.SetColumnIfExists("thumbnail",
          inImage.scaleStreamImageTo(this._scaleWidth, this._scaleHeight));
      }
      yield return output.AsReadOnly();
    }
  }
}

Là encore, l’extracteur a besoin de voir l’intégralité du fichier et fonctionne sur l’entrée. BaseStream, mais crée une seule Image dans la mémoire, à la différence du script dans Figure 12. L’extracteur de recherche également les colonnes demandées et traite uniquement les données pour les noms de colonne demandé à l’aide de la méthode d’extension SetColumnIfExists.

Pour plus d’informations, consultez le projet Visual Studio sur notre site GitHub bit.ly/2dngXCE.

Traitement des données spatiales

Dans cet exemple, je vais montrer comment utiliser l’assembly de type SQL Server Spatial Microsoft.SqlServer.Types.dll U-SQL. En particulier, je souhaite utiliser les fonctions de bibliothèque spatiale dans les scripts U-SQL en tant que fonctions définies par l’utilisateur. Comme dans le cas de l’extracteur JSON indiqué précédemment, cela signifie que vous souhaitez inscrire un assembly existant dans U-SQL sans avoir à écrire votre propre assembly.

Tout d’abord, vous devez télécharger et installer l’assembly à partir du pack de fonctionnalités SQL Server 2016 (bit.ly/2dZTw1k). Sélectionnez la version 64 bits du programme d’installation (ENU\x64\SQLSysClrTypes.msi) pour vous assurer de qu'avoir la version 64 bits des bibliothèques.

Le programme d’installation installe l’assembly managé Microsoft.SqlServer.Types.dll dans C:\Program Files (x86) \Microsoft SQL Server\130\SDK\Assemblies et de l’assembly natif SqlServerSpatial130.dll dans \Windows\System32\. Ensuite, téléchargez les assemblys dans votre magasin de LAC de données Azure (par exemple, dans un dossier appelé /upload/asm/spatial). Étant donné que le programme d’installation a installé la bibliothèque native dans le c:\Windows\System32 dossier système, vous devez vous assurer que vous copiez SqlServerSpatial130.dll à partir de ce dossier avant de le télécharger ou assurez-vous que l’outil que vous utilisez n’effectue pas la Redirection du système de fichiers (bit.ly/1TYm9YZ) sur les dossiers système. Par exemple, si vous souhaitez télécharger avec l’Explorateur de fichiers ADL actuelle de Visual Studio, vous devrez copier le fichier dans un autre répertoire tout d’abord, dans le cas contraire, au moment de la rédaction de cet article, vous obtenez la version 32 bits téléchargée (étant donné que Visual Studio est une application 32 bits qui effectue la Redirection du système de fichiers dans sa fenêtre de sélection de fichier de téléchargement ADL) , et lorsque vous exécutez un script U-SQL qui appelle dans l’assembly natif, vous obtiendrez l’erreur suivante (interne) lors de l’exécution : « L’exception interne à partir de l’expression de l’utilisateur : Une tentative a été effectuée pour charger un programme avec un format incorrect. (Exception de HRESULT: 0x8007000b). »

Après avoir téléchargé les fichiers de deux assembly, enregistrez-les dans une base de données SQLSpatial avec ce script :

DECLARE @ASSEMBLY_PATH string = "/upload/asm/spatial/";
DECLARE @SPATIAL_ASM string = @ASSEMBLY_PATH+"Microsoft.SqlServer.Types.dll";
DECLARE @SPATIAL_NATIVEDLL string = @ASSEMBLY_PATH+"SqlServerSpatial130.dll";
CREATE DATABASE IF NOT EXISTS SQLSpatial;
USE DATABASE SQLSpatial;
DROP ASSEMBLY IF EXISTS SqlSpatial;
CREATE ASSEMBLY SqlSpatial
FROM @SPATIAL_ASM
WITH ADDITIONAL_FILES =
  (
    @SPATIAL_NATIVEDLL
  );

Remarque Dans ce cas vous inscrivez un assembly U-SQL et incluez l’assembly natif comme une forte dépendance à l’assembly U-SQL. Pour utiliser les assemblys spatiales, vous devez uniquement référence l’assembly U-SQL et le fichier supplémentaire automatiquement sera disponible pour l’assembly. Figure 15 montre un exemple simple de script à l’aide de l’assembly spatiale.

Figure 15 l’utilisation des fonctionnalités spatiales dans U-SQL

REFERENCE SYSTEM ASSEMBLY [System.Xml];
REFERENCE ASSEMBLY SQLSpatial.SqlSpatial;
USING Geometry = Microsoft.SqlServer.Types.SqlGeometry;
USING Geography = Microsoft.SqlServer.Types.SqlGeography;
USING SqlChars = System.Data.SqlTypes.SqlChars;
@spatial =
    SELECT * FROM (VALUES
                   // The following expression is not using the native DDL
                   ( Geometry.Point(1.0,1.0,0).ToString()),   
                   // The following expression is using the native DDL
                   ( Geometry.STGeomFromText(
                     new SqlChars("LINESTRING (100 100, 20 180, 180 180)"),
                     0).ToString())
                  ) AS T(geom);
OUTPUT @spatial
TO "/output/spatial.csv"
USING Outputters.Csv();

La bibliothèque de Types SQL a une dépendance sur l’assembly System.Xml, vous devez le référencer. En outre, certaines méthodes sont à l’aide de types System.Data.SqlTypes au lieu de types c# intégrés. System.Data est déjà inclus par défaut, vous pouvez simplement faire référence le type SQL nécessaire. Le code dans Figure 15 est disponible sur notre site GitHub bit.ly/2dMSBm9.

Pour résumer : Quelques conseils et les meilleures pratiques pour les opérateurs

Cet article, pendant seulement gratter la surface des fonctionnalités d’extensibilité puissante de SQL-U, a montré comment le mécanisme d’extensibilité U-SQL vous permet de réutiliser le code spécifique à un domaine existant lors de l’utilisation de l’infrastructure d’extensions U-SQL à l’échelle le traitement out sur le volume de données volumineuses classique.

Mais un puissant outil permettre également être utilisés à mauvais escient facilement, voici quelques conseils et les conseils pratiques.

Tandis que les formats de données personnalisés doivent souvent un extracteur personnalisé et potentiellement un outputter, il doit considérer très soigneusement si le format de données peut être extraites en parallèle (tels que les formats de type CSV) ou si le traitement a besoin de voir toutes les données dans une instance unique d’opérateur. En outre, rendre les opérateurs génériques suffisamment et le traitement se produit uniquement si une colonne spécifique est demandée peut également améliorer les performances.

Lorsque vous envisagez des opérateurs tels que processeurs, raccords de réduction, combinateurs et appliers, il est vivement recommandé à tout d’abord envisager une solution de U-SQL pure qui tire parti des opérateurs intégrés. Par exemple, plage raccord de réduction écrit le script abordé plus haut peut réellement être avec une utilisation intelligente des fonctions de classement et de fenêtrage. Voici quelques raisons pourquoi vous voudrez prendre en compte les opérateurs :

  • La logique doit accéder à l’entrée ou sortie du schéma de l’ensemble de lignes en cours de traitement de manière dynamique. Par exemple, créer un document JSON pour les données de la ligne où les colonnes ne sont pas connus à l’avance.
  • Une solution à l’aide de plusieurs fonctions définies par l’utilisateur dans l’expression SELECT crée trop sollicitation de la mémoire et vous pouvez écrire votre code d’être plus économe en mémoire dans un processeur UDO.
  • Vous devez un agrégateur ordonné ou un agrégateur qui génère plusieurs lignes par groupe, et vous ne pouvez pas écrire avec les fonctions de fenêtrage.

Lorsque vous utilisez des opérateurs, vous devez toujours conserver les conseils suivants à l’esprit :

  • Utilisez la clause en lecture SEULE pour permettre l’envoi de prédicats via les opérateurs.
  • Utiliser la clause OBLIGATOIRE pour permettre le nettoyage de colonne être envoyées via les opérateurs.
  • Indicateur cardinalité dans votre expression de requête qui utilise un OPÉRATEUR, l’optimiseur de requête choisira un plan incorrect.

Michael Rysest responsable de programme principal chez Microsoft.  Il a fait du traitement des données et des langages de requête depuis les années 1980. Il a représentés Microsoft SQL et XQuery conception et a utilisé SQL Server au-delà relationnel XML, géographiques et recherche sémantique. Actuellement il travaille sur les langages de requête de données volumineuses telles que la PORTÉE et U-SQL lorsqu’il est ne bénéficiant pas des temps avec sa famille fonds marins ou à autocross. Vous pouvez le suivre sur Twitter : @MikeDoesBigData.

Merci aux experts techniques Microsoft suivants d'avoir relu cet article : Clemens Szyperski, Ed Triou, Reddy Saveen et Michael Kadaner
Clemens Szyperski est un responsable principal de groupe chez Microsoft. Depuis des décennies, sa passion a été spécialisés langues, des outils et des méthodes qui facilitent la construction des systèmes logiciels complexes. Actuellement, il dirige les équipes SQL Azure Data Lake U et étendue, à moins qu’il navigue immédiatement avec sa famille. Vous pouvez le suivre sur Twitter : @ClemensSzy.

Ed Triou est un responsable de développement principal chez Microsoft. Pour les 20 dernières années qu’il s’est concentré sur la programmabilité des données (ODBC, OLEDB, ADO.NET, JDBC, PHP et EDM), avec une spécialisation dans les compilateurs et les langages de requête (IDL, t-SQL, LINQ to SQL/entités, eSQL, ÉTENDUE et U-SQL).  Actuellement, il dirige les équipes de compilateur et langage SQL-U, essaie de garder une avance sur nos entreprises externes et internes qui dépendent quotidiennement ADL et Cosmos à grande échelle exaoctet.

Reddy Saveen est responsable de programme principal chez Microsoft et se concentre sur la conception et la création de la plate-forme de LAC de données Azure, les composants et prenant en charge tous les services de cloud de Microsoft des données volumineuses. Saveen contient une évaluation égale à 100 % de l’exécution de métal ENGRENAGE solide V: La difficulté fantôme. Vous pouvez le suivre sur Twitter : @saveenr

Michael Kadaner est ingénieur logiciel principal chez Microsoft. En dépit des dizaines d’années d’expérience dans divers domaines de développement informatique et des logiciels d’ordinateur, qu’il prétend que l’écriture de programmes est une art précise et le logiciel peut être exempt de bogue. Sa passion true est résoudre les problèmes complexes d’algorithmiques et de conception et implémentation des solutions dans le code concis et élégant approprié par sa conception il partage son temps libre entre les projets de lecture et personnalisables.


Discussion sur cet article dans le forum MSDN Magazine