Connect(); 2016

Volume 31 Numero 12

Il presente articolo è stato tradotto automaticamente.

Connect();: App intelligenti - Estendibilità nelle applicazioni Big Data U-SQL

Da Michael Rys; 2016

Lo stato attivo tradizionale sull'indirizzamento di V big dei Big Data, volume, velocità e diversi, ovvero durante Big Data elaborazione è concentrato principalmente per fornire una piattaforma scalabile per elaborare il volume di dati, sull'aggiunta di funzionalità di elaborazione in tempo quasi reale e di offrire la possibilità di elaborare una varietà di formati di dati di input, da file CSV in JSON per i formati binari personalizzati. Una serie che è stata spesso più o meno di un secondo momento è la varietà associata con l'elaborazione dati personalizzata, non solo in termini di formato, ma anche la possibilità di semplificare estendere l'analisi con algoritmi personalizzati mantenendo la natura dell'esperienza del linguaggio di query dichiarativa.

Alcuni linguaggi di query ed elaborazione dati moderni hanno iniziato a risolvere il problema. In particolare, U-SQL è stato progettato da zero di combinare la potenza dichiarativa di un linguaggio basato su SQL con la flessibilità di utilizzo le librerie di codice esistente e lo sviluppo di nuovi algoritmi personalizzati.

In un articolo precedente (bit.ly/1OtXM2K), introdotto U-SQL ed è stato illustrato come tramite Microsoft .NET Framework digitare sistema insieme a c#-linguaggio delle espressioni in base U SQL rende più semplice per estendere l'analisi con le espressioni di codice personalizzato. Ho spiegato come utilizzare gli assembly c# per definire le funzioni definite dall'utente (UDF) e usarli negli script delle query SQL U.

U-SQL non solo consente di aggiungere c# funzioni personalizzate, ma fornisce inoltre un framework in cui è possibile aggiungere i proprio operatori definiti dall'utente (UDO), ad esempio il proprio estrattori, outputters e operatori di set di righe, ad esempio processori, appliers, REDUCER e combiners personalizzato. Il framework è costituito da due parti:

  1. Interfacce di .NET che forniscono il contratto per la creazione di questi operatori in modo tale che è possibile concentrarsi sul codice, lasciando l'esecuzione di scalabilità orizzontale a U-SQL. Si noti che il codice della logica di business non debba essere implementati in .NET, come vedremo più avanti.
  2. Espressioni di U-SQL, ad esempio ESTRARRE e RIDUZIONE che richiamano gli operatori personalizzati e di eseguirle su larga scala ai dati.

In questo articolo verrà compilare nell'articolo precedente e Mostra come è possibile utilizzare i meccanismi di estensibilità di U-SQL per elaborare un'ampia gamma di dati diverso da JSON a dati immagine. Verrà anche illustrato come aggiungere operatori personalizzati.

Gestire il codice personalizzato in U-SQL

Prima di iniziare con alcuni degli esempi, possibile comprendere meglio come U-SQL può utilizzare il codice personalizzato.

Come accennato, U-SQL seguente c# con il linguaggio delle espressioni scalari, che viene utilizzato in posizioni quali predicati U-SQL e nelle espressioni in una clausola select. Per il codice personalizzato siano visibili al compilatore U-SQL, il codice deve essere inserito in un assembly .NET che lo script SQL U deve fare riferimento. Per essere in grado di fare riferimento all'assembly, si deve essere stato precedentemente registrato nel servizio metadati U-SQL utilizzando un'istruzione CREATE ASSEMBLY.

Registrazione e gli assembly di riferimento SQL U è preferibile utilizzare gli strumenti di Azure Data Lake per Visual Studio (aka.ms/adltoolsvs), che rendono più semplice creare e registrare gli assembly che funzionano con U-SQL. Se è scrivere codice personalizzato in un progetto "libreria di classi (per applicazione U-SQL)" (vedere figura 1), è possibile quindi scrivere il codice e compilare il progetto e registrare direttamente i file DLL dell'assembly generato con il pulsante destro (vedere figura 2).

Progetto libreria di classi (per applicazione U-SQL)
Figura 1 progetto libreria di classi (per applicazione U-SQL)

Registrazione di un Assembly di U-SQL
Figura 2 registrazione di un Assembly di U-SQL

È sufficiente nello Script SQL U è l'istruzione di ASSEMBLY di RIFERIMENTO per rendere le classi pubbliche e i metodi utilizzati nello script di U-SQL, come illustrato nella figura 3.

Figura 3, che fa riferimento a una funzione definita dall'utente da un Assembly personalizzato

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;
...

Utilizzo di codice esistente con gli assembly SQL U spesso è opportuno utilizzare librerie di codice esistenti o codice anche non .NET. Se si desidera utilizzare codice non .NET, ad esempio, una libreria nativa o anche un completamente diverse language runtime quali Python o JavaScript, è necessario racchiudere il codice non .NET con un livello interoperabilità c# che verrà chiamato da U-SQL e che quindi chiama il codice non .NET, il marshalling dei dati tra i componenti e implementazione di un contratto di interfaccia operatore definito dall'UTENTE. In questo caso, gli elementi di codice non .NET, ad esempio le DLL native o i file di runtime diversi devono essere aggiunti come file aggiuntivi. Questa operazione può essere eseguita nell'opzione File aggiuntivi della registrazione dell'assembly. Questi file vengono distribuiti automaticamente a ogni nodo quando l'assembly .NET viene fatto riferimento in uno script e viene rese disponibile nella directory di lavoro dell'assembly .NET in locale in tale nodo.

Per utilizzare librerie .NET esistenti, è necessario registrare le librerie di codice esistente come dipendenze gestito in un assembly personalizzato, o, se si riutilizza una libreria che è possibile utilizzare direttamente in U-SQL, ovvero registrarlo direttamente nel database SQL di U. In entrambi i casi, lo script deve fare riferimento a tutti gli assembly .NET necessari per lo script.

Illustrerò alcuni esempi di queste opzioni di registrazione nella parte rimanente dell'articolo, come illustrato alcuni scenari di codice personalizzato in cui è opportuno utilizzare il modello di estendibilità. Questi scenari includono: unione di intervalli sovrapposti con un riduttore personalizzato, l'elaborazione di documenti JSON, l'elaborazione dei dati di immagine e l'elaborazione dei dati spaziali. Mi occuperò ogni volta.

Unione di intervalli sovrapposti con un riduttore personalizzato

Si supponga di che avere un file di log che tiene traccia di un utente interagisce con il servizio. Si supponga inoltre che un utente può interagire con il servizio in diversi modi (ad esempio, mediante l'esecuzione di ricerche di Bing da più dispositivi o le finestre del browser). Come parte del processo di U-SQL che consente di preparare il file di log per una successiva analisi, si desidera unire intervalli sovrapposti.

Ad esempio, se il file di log input appare come figura 4, quindi si desidera unire intervalli sovrapposti per ogni utente in figura 5.

Figura 4 File di Log con gli intervalli di tempo di sovrapposizione

Ora di inizio  Ora di fine  Nome utente
5:00 AM  6:00 AM  ABC
5:00 AM  6:00 AM  XYZ
8:00 AM  9:00 AM  ABC
8:00 AM  10:00 AM  ABC
10:00 AM  2:00 PM  ABC
7:00 AM  11:00 AM  ABC
9:00 AM  11:00 AM  ABC
11:00 AM  11:30 AM  ABC
11:40 PM  11:59 PM  FOO
11:50 PM  0:40 AM  FOO

Figura 5 File di Log dopo l'unione di sovrapposizione degli intervalli di tempo

Ora di inizio  Ora di fine  Nome utente
5:00 AM  6:00 AM  ABC
5:00 AM  6:00 AM  XYZ
7:00 AM  2:00 PM  ABC
11:40 PM  0:40 AM  FOO

Se si esamina il problema, prima noterete si desidera definire come un'aggregazione definita dall'utente per combinare gli intervalli di tempo sovrapposti. Tuttavia, se si esamina i dati di input, si noterà che poiché i dati non ordinati, è sarà necessario mantenere lo stato per tutti gli intervalli possibili e quindi unire intervalli non contigui, come vengono visualizzati gli intervalli di bridging o devi prenotate gli intervalli per ogni nome utente per l'unione degli intervalli di più semplice.

L'aggregazione ordinato è più semplice per la scalabilità orizzontale, ma U-SQL non fornisce ordinati aggregatori definito dall'utente (aggregazioni definite dall'utente). Inoltre, aggregazioni definite dall'utente in genere producono una riga per ogni gruppo, mentre in questo caso, è possibile disporre di più righe per ogni gruppo se gli intervalli sono intervalli non contigui.

Per fortuna, U-SQL fornisce un operatore definito dall'UTENTE scalabile denominata un riduttore (bit.ly/2evGsDA) che può aggregare un set di righe in base a un set di chiavi di raggruppamento con codice personalizzato.

Innanzitutto iniziare a scrivere la logica di U-SQL in cui ReduceSample.RangeReducer è la riduzione definite dall'utente (UDO riduttore) dall'assembly RangeReducer e i dati di log si trova in /Samples/Blogs/MRys/Ranges/ranges.txt il file (bit.ly/2eseZyw) e viene utilizzato "-" come delimitatore di colonna. Il codice è il seguente:

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'espressione di RIDUZIONE ha il set di righe @in presorts come input, le partizioni in base alla colonna utente, le partizioni in base ai valori nella colonna di inizio e applica RangeReducer, producendo lo stesso schema di set di righe nell'output. Poiché la riduzione regola solo l'intervallo di dall'inizio alla fine, in realtà non tocchi la colonna utente, in modo da contrassegnarlo come di sola LETTURA. Ciò offre il framework di riduzione dell'autorizzazione per passare i dati automaticamente per la colonna e, in cambio consente al processore di query SQL U applicare in modo aggressivo ottimizzazioni attorno alle colonne di sola lettura, ad esempio ai predicati di push in una colonna di sola lettura prima la riduzione.

Il modo per scrivere un riduttore consiste nell'implementare un'istanza di Microsoft.Analytics.Interfaces.IReducer. In questo caso, poiché non è necessario fornire i parametri, è sufficiente sovrascrivere il metodo astratto di riduzione. È possibile copiare il codice in una libreria c# di U-SQL e la registrazione dell'assembly RangeReducer come illustrato in precedenza. Figura 6 viene illustrata l'implementazione di RangeReducer. Si noti che in alcuni esempi di codice sono stati alterati consigliate rientro normale codice che a causa dei limiti di spazio.

Figura 6, C# implementazione di 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'espressione SQL U RIDURRE verrà applicato il metodo di riduzione di una volta per ogni chiave di partizione distinta in parallelo. Il parametro di input in questo modo conterrà solo le righe per un gruppo specifico e l'implementazione può restituire zero al numero di righe come output.

Poiché la clausola di ordinamento PRELIMINARE garantisce che le righe vengono ordinate, che la logica interna può assumere che i dati vengono ordinati e poiché la colonna utente è contrassegnata come di sola LETTURA, la colonna verrà trasmesse automaticamente ed è possibile scrivere codice UDO più genericamente focalizzando l'attenzione solo alle colonne che si desidera trasformare.

Se si applica a questo punto la riduzione in un ampio set di dati e se alcuni utenti potrebbero utilizzando il sistema molto più frequente rispetto ad altri, si verificherà un elemento denominato dati dello sfasamento di orario in cui alcuni utenti hanno le partizioni di grandi dimensioni e altri utenti solo le partizioni di piccole dimensioni. Poiché per visualizzare tutti i dati per la partizione è garantito che il contratto del reducer, tutti i dati devono essere mescolati a tale nodo e leggere in un'unica chiamata. A causa di questo requisito, tali dati inclinazione nel migliore dei casi possono comportare alcune partizioni richiede molto più tempo rispetto ad altri per elaborare e nel peggiore dei casi possono comportare alcune riduttori sta esaurendo la memoria disponibile e le risorse di tempo (un vertice U SQL si verifica un timeout dopo l'esecuzione per circa cinque ore).

Se la semantica di riduzione è associative e commutative e lo schema di output è lo stesso schema di input, quindi una riduzione può essere contrassegnato come ricorsiva, quale il motore di query suddividere i gruppi di grandi dimensioni in più piccoli in modo ricorsivo e i sottogruppi applica la riduzione in questi gruppi per calcolare il risultato finale. Questa applicazione ricorsiva consente la riduzione per bilanciare meglio e parallelizzare in presenza dello sfasamento di dati. Un riduttore è contrassegnato come ricorsiva utilizzando l'annotazione di proprietà 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

In questo caso, la riduzione può essere contrassegnata come ricorsiva per migliorare la scalabilità e prestazioni, presupponendo che l'elaborazione verrà conservata l'ordinamento tra le righe in ogni chiamata ricorsiva.

È possibile trovare un progetto di Visual Studio per l'esempio sul nostro repository GitHub in bit.ly/2ecLe5B.

L'elaborazione di documenti JSON

Uno dei formati di dati più frequenti dopo i file di testo delimitato da virgole è JSON. A differenza dei formati di file CSV, U-SQL non fornisce un'estrazione JSON incorporato. Tuttavia, la community di SQL U ha fornito un assembly di esempio in bit.ly/2d9O4va che offre supporto per l'estrazione e l'elaborazione di documenti JSON e XML.

Questa soluzione utilizza una libreria di Json.NET di Newtonsoft (bit.ly/2evWJbz) per il sollevamento JSON pesante e System. XML per l'elaborazione XML. L'assembly può estrarre dati da un documento JSON tramite il JsonExtractor (bit.ly/2dPARsM), un documento JSON e dividere il un SqlMap per consentire la navigazione e la scomposizione dei documenti JSON con la funzione JsonTuple (bit.ly/2e8tSuX) e infine trasformare un set di righe in un file in formato JSON con il JSONOutputter (bit.ly/2e4uv3W).

Si noti che l'assembly è progettato per essere un processore JSON generico, il che significa non ha alcuna supposizione sulla struttura del documento JSON e deve essere resiliente alla natura semistrutturata di JSON, tra cui heterogeneously tipizzato elementi (scalari e tipi di dati strutturati, diversi per lo stesso elemento, elementi mancanti e così via). Se si conosce che i documenti JSON conformi a uno schema specifico, è possibile creare eventualmente un'estrazione JSON più efficiente.

A differenza dell'esempio di riduzione in precedenza, in cui si scrive un assembly personalizzato da distribuire successivamente, in questo caso la soluzione è pronta per essere utilizzato. È possibile caricare la soluzione dal nostro repository GitHub in Visual Studio e compilare e distribuire manualmente oppure è possibile trovare le DLL nella directory bin\Debug della soluzione.

Come accennato in precedenza, la dipendenza non di sistema richiede che sia il Samples.Format e gli assembly Json.NET devono essere registrati nell'archivio di metadati SQL U (è possibile selezionare l'assembly Newtonsoft come dipendenza gestiti durante la registrazione dell'assembly di formato utilizzando lo strumento di Visual Studio) e che sia necessario fare riferimento se si desidera elaborare documenti JSON. Supponendo di avere installato l'assembly JSON nel catalogo SQL U sotto il nome [Microsoft.Analytics.Samples.Formats] e [Newtonsoft] nel database di SQL U JSONBlog (vedere figura 7), è possibile utilizzare gli assembly facendovi riferimento all'inizio degli script con:

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

Registrazione dell'Assembly di formati in Visual Studio
Figura 7 registrando l'Assembly di formati in Visual Studio

L'estrattore JSON implementa l'interfaccia IExtractor U-SQL. Poiché JSON documenti devono essere completamente analizzato per assicurarsi che siano in formato corretto, sarà necessario un file contenente un singolo documento JSON da elaborare in un singolo vertice di estrazione. Pertanto, si indica che deve visualizzare il contenuto completo del file impostando la proprietà AtomicFileProcessing su true l'estrattore (vedere figura 8). L'utilità di estrazione può essere chiamato con un parametro facoltativo denominato rowpath che consente di identificare gli oggetti JSON che verranno ognuna mappati a una riga utilizzando un'espressione JSONPath (bit.ly/1EmvgKO).

Figura 8 l'estrattore 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'implementazione di estrazione passerà il flusso di input che alimenta il framework di estrazione U-SQL in utilità di estrazione per il Json.NET JsonTextReader. Quindi utilizza il rowpath per ottenere la sottostruttura ad albero viene mappata a una riga mediante SelectChildren. Poiché gli oggetti JSON possono essere eterogenei, il codice ha restituito un oggetto JObject generico anziché JArray posizionale o valori scalari.

Si noti che questo programma di estrazione carica il documento JSON in memoria. Se il documento è troppo grande, è possibile che una condizione di memoria esaurita. In tal caso, è necessario scrivere il proprio programma di estrazione di flussi di scorrere il documento senza dover caricare l'intero documento in memoria.

Ora utilizziamo l'estrattore JSON e la funzione di tupla JSON per analizzare il documento JSON complesso da /Samples/Blogs/MRys/JSON/complex.json (bit.ly/2ekwOEQ) fornito figura 9.

Documento di esempio JSON figura 9 A

[{
  "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"
     }]
   }
  }
}]

Il formato è una matrice di persona "oggetti" (tecnicamente con una chiave singola persona ogni) che a sua volta contiene alcune proprietà persona e risolvere gli oggetti. Lo script SQL U in figura 10 estrae una riga per ogni combinazione di indirizzo /.

Figura 10 U-SQL Script l'elaborazione del documento JSON di esempio dalla figura 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();

Si noti che lo script passa .person l'espressione [*] JSONPath per l'estrazione, generando in tal modo una riga per ogni campo persona nella matrice di primo livello. Lo schema di ESTRAZIONE è utilizzato dall'utilità di estrazione per ottenere le proprietà dell'oggetto risultante in colonne. Poiché il campo di indirizzi è esso stesso un documento JSON nidificato, la prima chiamata della funzione JsonTuple crea una mappa che contiene gli oggetti di indirizzo, che vengono quindi mappati a una riga per ogni indirizzo con l'espressione TRA APPLICARE ESPLOSO. Infine, tutte le proprietà dell'indirizzo vengono proiettate da dati della mappa del tipo per fornire il set di righe, come illustrato nella figura 11.

Figura 11, il set di righe generato dall'elaborazione del documento JSON dalla figura 9

123456 Persona 1 2 Via 2 AB 1234 Città 1
123456 Persona 1 2 Via 2 CD 5678 City 2
798 Persona 2 1 Via 1 AB 1234 Città 1
798 Persona 2 4 Via e numero civico 7 98799 City 3

È possibile trovare un progetto di Visual Studio di esempio e altri scenari di elaborazione JSON, inclusi più documenti JSON in un file, nel nostro oggetto repository GitHub in bit.ly/2dzceLv.

L'elaborazione dei dati di immagine

In questo esempio, sono l'elaborazione di alcuni dati non strutturati di maggiori dimensioni: le immagini. In particolare, si desidera elaborare immagini JPEG ed estrarre alcune proprietà JPEG EXIF, nonché creare un'anteprima dell'immagine. Per fortuna, .NET fornisce un'ampia gamma di funzionalità nella classe System. Drawing di elaborazione delle immagini. Pertanto è necessario fare è compilazione U-SQL le funzioni di estensione e operatori, la delega dell'elaborazione a queste classi JPEG.

Esistono diversi metodi per eseguire questa operazione. Il tentativo iniziale potrebbe caricare tutte le immagini come matrici di byte in un set di righe e quindi applicare singole funzioni definite dall'utente per ogni proprietà estrarre e creare l'immagine di anteprima, come illustrato nella figura 12.

Figura 12 elaborazione delle immagini in U-SQL per il caricamento delle immagini in righe

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");

Tuttavia, questo approccio presenta alcuni svantaggi:

  • Le righe di U-SQL possono essere al massimo 4MB di dimensioni, limitando in tal modo la soluzione alle immagini di dimensioni di 4MB (esclusa la dimensione delle altre colonne).
  • Ognuna delle chiamate di funzione può aggiungere per l'utilizzo di memoria e richiede che scorre la matrice di byte tramite l'elaborazione di U-SQL.

Pertanto, un approccio migliore è di eseguire la creazione di estrazione e anteprima di proprietà direttamente all'interno di estrazione personalizzata. Figura 13 viene illustrato uno script SQL U modificato.

Figura 13 l'elaborazione di immagini in SQL U estraendo le funzioni con un programma di estrazione

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();

Questo script consente di estrarre le proprietà e l'anteprima da immagini specificate dal modello di set di file (bit.ly/2ektTY6): esempi/Data/immagini / {nome}. { formato}. L'istruzione SELECT limita l'estrazione di file JPEG tramite un predicato solo nella colonna formato che consente di eliminare tutti i file non JPEG l'estrazione (query optimizer verrà applicata solo l'utilità di estrazione per i file che soddisfano il predicato sulla colonna formato). L'estrattore consente di specificare le dimensioni dell'anteprima. Lo script quindi restituisce le funzionalità in un file CSV e utilizza un semplice outputter del livello di flusso di byte, per creare un file di anteprima per una delle immagini in scala ridotta.

Figura 14 viene illustrata l'implementazione dell'utilità di estrazione.

Figura 14 l'estrattore funzionalità immagine

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'estrattore nuovamente deve visualizzare l'intero file e opera nell'input. BaseStream ma ora crea solo un'immagine in memoria, a differenza degli script in figura 12. L'estrattore controlla inoltre per ognuna delle colonne richieste ed elabora solo i dati per i nomi di colonna richiesto tramite il metodo di estensione SetColumnIfExists.

Per ulteriori informazioni, vedere il progetto di Visual Studio sul nostro sito GitHub all'indirizzo bit.ly/2dngXCE.

L'elaborazione dei dati spaziali

In questo esempio verrà illustrato come utilizzare l'assembly del tipo spaziale di SQL Server Microsoft.SqlServer.Types.dll U-SQL. In particolare, si desidera utilizzare le funzioni della libreria spaziale negli script SQL U come funzioni definite dall'utente. Come nel caso dell'estrattore JSON discusso in precedenza, questo significa che si desidera registrare un assembly già esistente in U-SQL senza la necessità di scrivere un proprio assembly.

In primo luogo, è necessario scaricare e installare l'assembly dal feature pack di SQL Server 2016 (bit.ly/2dZTw1k). Selezionare la versione a 64 bit del programma di installazione (ENU\x64\SQLSysClrTypes.msi) consente di ottenere la versione a 64 bit delle librerie.

Il programma di installazione installa l'assembly gestito Microsoft.SqlServer.Types.dll in C:\Program Files (x86) \Microsoft SQL Server\130\SDK\Assemblies e l'assembly nativo SqlServerSpatial130.dll in \Windows\System32\.. Successivamente, caricare gli assembly nell'archivio Azure Data Lake (ad esempio, in una cartella denominata /upload/asm/spatial). Poiché il programma di installazione ha installato la libreria nativa in c:\Windows\System32 la cartella del sistema, è necessario assicurarsi che copiare SqlServerSpatial130.dll da questa cartella prima di caricarlo oppure assicurarsi di utilizzare lo strumento non eseguita il reindirizzamento del File System (bit.ly/1TYm9YZ) per le cartelle di sistema. Ad esempio, se si desidera caricarlo con le risorse di File corrente di Visual Studio ADL, sarà necessario copiare il file in un'altra directory in primo luogo, in caso contrario, al momento della stesura di questo articolo, si otterrà la versione a 32 bit caricata (poiché Visual Studio è un'applicazione a 32 bit che esegue il reindirizzamento del File System nella relativa finestra di selezione ADL caricamento file) , e quando si esegue uno script di U-SQL che chiama in un assembly nativo, si otterrà il seguente errore (interno) in fase di esecuzione: "L'eccezione interna da espressione utente: È stato effettuato il tentativo di caricare un programma con un formato non corretto. (Eccezione da HRESULT: 0x8007000b)."

Dopo aver caricato i file di due assembly, registrarli in un database denominato SQLSpatial con questo 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
  );

Si noti in questo caso si limita a Registra un assembly di U-SQL e includere l'assembly nativo come una dipendenza sicura all'assembly U-SQL. Per utilizzare gli assembly spaziali, è necessario solo fare riferimento all'assembly U-SQL e il file aggiuntivo automaticamente saranno disponibile per l'assembly. Figura 15 viene illustrato uno script di esempio semplice utilizzando l'assembly spaziale.

Figura 15 utilizzando la funzionalità spaziale di 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 libreria di tipi SQL presenta una dipendenza dall'assembly System. XML, pertanto è necessario farvi riferimento. Inoltre, alcuni dei metodi sono utilizzando i tipi di System.Data.SqlTypes anziché i tipi c# incorporati. Poiché System. Data è già incluso per impostazione predefinita, è possibile semplicemente fare riferimento il tipo SQL necessario. Il codice in figura 15 è disponibile sul sito GitHub bit.ly/2dMSBm9.

Conclusioni: Alcuni suggerimenti e procedure consigliate per operatori

In questo articolo, mentre solo le potenzialità delle funzionalità di estendibilità di U-SQL, è stato illustrato come il meccanismo di estendibilità U-SQL consente di riutilizzare il codice specifico di dominio esistente mentre si utilizza il framework di estensione U-SQL per ingrandire l'elaborazione out in un volume di dati Big tipico.

Tuttavia, un potente strumento anche l'utilizzo improprio con facilità, in modo Ecco alcuni suggerimenti e le procedure consigliate.

Mentre i formati di dati personalizzato è spesso necessario un programma di estrazione personalizzata e potenzialmente un outputter, uno necessario considerare molto attentamente se il formato dei dati può essere estratti in parallelo (ad esempio i formati di tipo CSV) o se l'elaborazione necessaria visualizzare tutti i dati in un'istanza del singolo operatore. Inoltre, rendendo gli operatori generici sufficiente l'elaborazione viene eseguita solo se viene richiesta una specifica colonna potenzialmente anche migliorare le prestazioni.

Quando si considerano UDO quali processori, riduttori, combiners e appliers, è consigliabile innanzitutto considerare una soluzione SQL U pura che sfrutta gli operatori incorporati. Ad esempio, effettivamente Impossibile scritto lo script di riduzione intervallo indicato in precedenza con alcune utilizzo intelligente delle funzioni di rango e windowing. Ecco alcuni motivi per cui è comunque consigliabile considerare UDO:

  • La logica deve accedere all'input o output dello schema del set di righe che viene elaborata in modo dinamico. Ad esempio, creare un documento JSON per i dati nella riga in cui le colonne non sono noti in anticipo.
  • È possibile scrivere il codice per essere più efficiente della memoria in un processore UDO una soluzione utilizzando diverse funzioni definite dall'utente nell'espressione SELECT creato troppa memoria.
  • È necessario un aggregatore ordinato o un aggregatore che produce più di una riga per ogni gruppo e non è possibile scrivere uno con le funzioni di windowing.

Quando si utilizza UDO, è consigliabile mantenere sempre i suggerimenti seguenti presente:

  • Utilizzare la clausola di sola LETTURA per consentire l'inserimento di predicati tramite operatori.
  • Utilizzare la clausola REQUIRED per consentire l'eliminazione di colonne da inserire tramite operatori.
  • Cardinalità di hint per l'espressione di query che utilizza un operatore definito dall'UTENTE, deve query optimizer di scegliere il piano non corretto.

Michael Rysè principal program manager presso Microsoft.  Eseguono l'elaborazione dati e i linguaggi di query poiché il 1980s. Egli ha rappresentati Microsoft XQuery e SQL progettazione e sia SQL Server oltre relazionale con XML, geospaziali e ricerca semantica. Attualmente lavora in linguaggi di query di Big Data, ad esempio l'AMBITO e U-SQL quando si ha tempo non è godono con familiari il mondo sommerso o autocross. Seguirlo su Twitter: @MikeDoesBigData.

Grazie per i seguenti esperti tecnici Microsoft per la revisione dell'articolo: Clemens Szyperski, Ed Triou, Saveen Reddy e Michael Kadaner
Clemens Szyperski è un principal group manager engineering di Microsoft. Per decenni, è stata la passione autentica linguaggi specializzati, strumenti e approcci che facilitano la costruzione di sistemi software complessi. Dirige attualmente i team di SQL Azure Data Lake U e ambito, a meno che non ha subito è che navigano con familiari. Seguirlo su Twitter: @ClemensSzy.

ed Triou è un responsabile dello sviluppo principale presso Microsoft. Negli ultimi 20 anni ha si è concentrato sul programmabilità dei dati (ODBC, OLEDB, ADO.NET, JDBC, PHP ed EDM), con una specializzazione per i compilatori e linguaggi di query (IDL, TSQl, LINQ to Entities/SQL, eSQL, AMBITO e U-SQL).  Attualmente dirige il team del compilatore e linguaggio di U-SQL, tenta di mantenere un passo avanti rispetto alle aziende il nostro interne ed esterne che ogni giorno dipendono ADL e Cosmos su larga scala exabyte.

Saveen Reddy è principal program manager presso Microsoft per progettare e creare i componenti della piattaforma Azure Data Lake: e si verifica nel supporto di tutti i servizi cloud di Microsoft Big Data. Saveen contiene una classificazione pari al 100% completamento per Metal Gear solida v Nel riquadro fantasma. Seguirlo su Twitter: @saveenr

Michael Kadaner è un principal software engineer di Microsoft. Nonostante decenni di esperienza in diverse aree dello sviluppo software e analisi scientifica dei computer, e afferma che la scrittura di programmi è un'immagine precisa e software può essere privo di errori. Ama true è risoluzione di problemi complessi algoritmici e progettazione e implementazione delle soluzioni di codice conciso ed elegante corretto in base alla progettazione divide il suo tempo libero tra progetti fai da te e lettura.


Viene illustrato in questo articolo nel forum di MSDN Magazine