Grundlegendes zu Apache Spark-Code für U-SQL-Entwickler

Wichtig

Azure Data Lake Analytics am 29. Februar 2024 eingestellt. In dieser Ankündigung erhalten Sie weitere Informationen.

Für Datenanalysen kann Ihr organization Azure Synapse Analytics oder Microsoft Fabric verwenden.

Dieser Abschnitt bietet allgemeine Informationen zum Transformieren von U-SQL-Skripts zu Apache Spark.

Grundlegende Informationen zu den Sprach- und Verarbeitungsparadigmen von U-SQL und Spark

Bevor Sie mit der Migration der U-SQL-Skripts von Azure Data Lake Analytics zu Spark beginnen, ist es hilfreich, die allgemeine Sprache und die Verarbeitungsphilosophien der beiden Systeme zu verstehen.

U-SQL ist eine deklarative Abfragesprache, die SQL ähnelt und auf einem Datenflussparadigma basiert. U-SQL ermöglicht es, Benutzercode, der in .NET (z. B. C#), Python oder R geschrieben wurde, ganz einfach einzubetten und zu erweitern. Benutzererweiterungen können einfache Ausdrücke oder benutzerdefinierte Funktionen implementieren, dem Benutzer aber auch die Möglichkeit bieten, so genannte benutzerdefinierte Operatoren zu implementieren, die wiederum angepasste Operatoren implementieren, um Transformationen und Extraktionen auf Rowsetebene auszuführen und Ausgaben zu schreiben.

Spark ist ein erweiterbares Framework, das verschiedene Sprachbindungen in Scala, Java, Python, .NET usw. bietet. Damit können Sie Code in einer dieser Sprachen schreiben, als Resilient Distributed Datasets (RDD) bezeichnete Datenabstraktionen sowie Datenrahmen und Datasets erstellen und dann eine LINQ-ähnliche domänenspezifische Sprache (Domain-Specific Language, DSL) verwenden, um diese zu transformieren. Spark bietet zudem SparkSQL als deklarative Untersprache für die Datenrahmen- und Datasetabstraktionen. Die DSL bietet zwei Kategorien von Vorgängen: Transformationen und Aktionen. Durch das Anwenden von Transformationen auf die Datenabstraktionen wird die Transformation nicht ausgeführt, sondern stattdessen der Ausführungsplan erstellt, der mit einer Aktion zur Auswertung übermittelt wird (z. B. Schreiben des Ergebnisses in eine temporäre Tabelle oder Datei oder Drucken des Ergebnisses).

Wenn Sie also ein U-SQL-Skript in ein Spark-Programm übersetzen, müssen Sie entscheiden, welche Sprache Sie verwenden möchten, um zumindest die Datenrahmenabstraktion (die derzeit am häufigsten verwendete Datenabstraktion) zu generieren, und ob Sie die deklarativen Dataflowtransformationen mithilfe von DSL oder SparkSQL schreiben möchten. In komplexeren Fällen müssen Sie Ihr U-SQL-Skript möglicherweise in eine Abfolge von Spark und anderen Schritten aufteilen, die mit Azure Batch oder Azure Functions implementiert werden.

Darüber hinaus bietet Azure Data Lake Analytics U-SQL in einer serverlosen Auftragsdienstumgebung, in der Ressourcen für jeden Auftrag zugewiesen werden, während Azure Synapse Spark, Azure Databricks und Azure HDInsight Spark entweder in Form eines Clusterdiensts oder mit sogenannten Spark-Poolvorlagen anbieten. Beim Transformieren Ihrer Anwendung müssen Sie die Auswirkungen berücksichtigen, die sich aus dem erstellen, skalieren, skalieren und Außerbetriebnahmen der Cluster oder Pools ergeben.

Transformieren von U-SQL-Skripts

Bei U-SQL-Skripts wird das folgende Verarbeitungsmuster angewendet:

  1. Daten werden entweder unter Verwendung der EXTRACT-Anweisung, der Angabe eines Speicherorts oder Dateisatzes und mit dem integrierten oder benutzerdefinierten Extractor und Schema aus unstrukturierten Dateien oder aber aus U-SQL-Tabellen (verwaltete oder externe Tabellen) gelesen. Sie wird als Rowset dargestellt.
  2. Die Rowsets werden in mehrere U-SQL-Anweisungen transformiert, die U-SQL-Ausdrücke auf die Rowsets anwenden und neue Rowsets erzeugen.
  3. Schließlich werden die resultierenden Rowsets entweder mit der OUTPUT-Anweisung, die den Speicherort und einen integrierten oder benutzerdefinierten Ausgabeoperator angibt, in Dateien oder aber in eine U-SQL-Tabelle ausgegeben.

Das Skript wird verzögert ausgewertet. Das bedeutet, dass jeder Extraktions- und Transformationsschritt in eine Ausdrucksbaumstruktur eingefügt und global ausgewertet wird (der Datenfluss).

Spark-Programme funktionieren ähnlich wie Spark-Connectors, die Sie verwenden, um Daten zu lesen und Datenrahmen zu erstellen, die Transformationen mithilfe der LINQ-ähnlichen DSL oder von SparkSQL auf die Datenrahmen anzuwenden und dann das Ergebnis in Dateien, temporäre Spark-Tabellen, einige Arten von Programmiersprachen oder die Konsole zu schreiben.

Transformieren von .NET-Code

Die Ausdruckssprache von U-SQL ist C# und bietet verschiedene Möglichkeiten, benutzerdefinierten .NET-Code mit benutzerdefinierten Funktionen, benutzerdefinierten Operatoren und benutzerdefinierten Aggregatoren hochzuskalieren.

Azure Synapse und Azure HDInsight Spark unterstützen jetzt nativ die Ausführung von .NET-Code mit .NET für Apache Spark. Das bedeutet, dass Sie möglicherweise einige oder alle Ihrer benutzerdefinierten .NET-Funktionen mit Spark wiederverwenden können. Beachten Sie jedoch, dass U-SQL .NET Framework verwendet, .NET für Apache Spark basiert hingegen auf .NET Core 3.1 oder höher.

U-SQL-UDOs (User-Defined Operators, benutzerdefinierte Operatoren) nutzen das U-SQL-UDO-Modell, um die horizontal skalierte Ausführung des Operatorcodes zu ermöglichen. Daher müssen UDOs neu in benutzerdefinierte Funktionen geschrieben werden, um in das Spark-Ausführungsmodell zu passen.

.NET für Apache Spark unterstützt derzeit keine benutzerdefinierten Aggregatoren. Daher müssen benutzerdefinierte U-SQL-Aggregatoren in benutzerdefinierte Spark-Aggregatoren übersetzt werden, die in Scala geschrieben wurden.

Wenn Sie die .NET für Apache Spark-Funktionen nicht nutzen möchten, müssen Sie Ihre Ausdrücke in einen entsprechenden Spark-, Scala-, Java- oder Python-Ausdruck, eine Funktion, einen Aggregator oder einen entsprechenden Connector umschreiben.

Auf jeden Fall gilt Folgendes: Wenn in Ihren U-SQL-Skripts sehr viel .NET-Logik vorhanden ist, wenden Sie sich an Ihren Microsoft-Kundenbetreuer, um weitere Informationen zu erhalten.

Die folgenden Details gelten für die verschiedenen Fälle der Verwendung von .NET und C# in U-SQL-Skripts.

Transformieren von C#-Inline-Skalarausdrücken in U-SQL

Die Ausdruckssprache von U-SQL ist C#. Viele der skalaren Inline-U-SQL-Ausdrücke werden nativ implementiert, um die Leistung zu verbessern, während komplexere Ausdrücke durch Aufrufen von .NET Framework ausgeführt werden können.

Spark verfügt über eine eigene skalare Ausdruckssprache (entweder als Teil der DSL oder in SparkSQL) und ermöglicht das Aufrufen von benutzerdefinierten Funktionen, die für die JVM-, .NET- oder Python-Runtime geschrieben sind.

Wenn Sie über Skalarausdrücke in U-SQL verfügen, sollten Sie zuerst den am besten geeigneten nativ verstandenen Spark-Skalarausdruck ermitteln, um eine optimale Leistung zu erzielen. Ordnen Sie danach die anderen Ausdrücke einer benutzerdefinierten Funktion der Spark-Runtimesprache Ihrer Wahl zu.

Beachten Sie, dass .NET und C# eine andere Typsemantik verwenden als die JVM- und Python-Runtimes und die Spark-DSL. Informationen zu den Unterschieden in den Typsystemen finden Sie weiter unten.

Transformieren von benutzerdefinierten .NET-Skalarfunktionen und benutzerdefinierten Aggregatoren

U-SQL bietet Möglichkeiten zum Aufrufen von beliebigen .NET-Skalarfunktionen und zum Aufrufen von in .NET geschriebenen benutzerdefinierten Aggregatoren.

Spark bietet auch Unterstützung für benutzerdefinierte Funktionen und benutzerdefinierte Aggregatoren der meisten Hostsprachen, die aus der Spark-DSL und aus SparkSQL aufgerufen werden können.

Wie bereits erwähnt, unterstützt .NET für Apache Spark benutzerdefinierte Funktionen, die in .NET geschrieben wurden, aber keine benutzerdefinierten Aggregatoren. Für benutzerdefinierte Funktionen kann .NET für Apache Spark verwendet werden, während benutzerdefinierte Aggregatoren in Scala für Spark erstellt werden müssen.

Transformieren von benutzerdefinierten Operatoren

U-SQL bietet verschiedene Kategorien benutzerdefinierter Operatoren (User-Defined Operators, UDOs) zum Extrahieren (Extractors), Ausgeben (Outputters), Reduzieren (Reducers), Verarbeiten (Processors), Anwenden (Appliers) und Kombinieren (Combiners), die in .NET (und bis zu einem gewissen Grad auch in Python und R) geschrieben werden können.

Spark bietet nicht das gleiche Erweiterbarkeitsmodell für Operatoren, verfügt aber für einige über gleichwertige Funktionen.

Das Spark-Äquivalent zu Extractors und Outputters sind Spark-Connectors. In der Spark-Community finden Sie äquivalente Connectors für viele U-SQL-Extractors. Für andere müssen Sie einen benutzerdefinierten Connector schreiben. Wenn der U-SQL-Extractor komplex ist und verschiedene .NET-Bibliotheken verwendet, ist es möglicherweise sinnvoller, einen Connector in Scala zu erstellen, der dank Interoperabilität Aufrufe in der .NET-Bibliothek ausführen kann, die die tatsächliche Verarbeitung der Daten übernimmt. In diesem Fall müssen Sie die .NET Core-Runtime im Spark-Cluster bereitstellen und sicherstellen, dass die .NET Standard 2.0-Bibliotheken, auf die verwiesen wird, kompatibel sind.

Die anderen Arten von U-SQL-UDOs müssen unter Verwendung der benutzerdefinierten Funktionen und Aggregatoren sowie des semantisch geeigneten Spark-DSL- oder SparkSQL-Ausdrucks neu geschrieben werden. Beispielsweise kann ein Prozessor einer SELECT-Instanz verschiedener UDF-Aufrufe zugeordnet werden, verpackt als Funktion, die einen Dataframe als Argument verwendet und einen Dataframe zurückgibt.

Transformieren von optionalen U-SQL-Bibliotheken

U-SQL bietet eine Reihe optionaler und Demobibliotheken, die Python-, R-, JSON-, XML-, AVRO-Unterstützung und einige Funktionen für Azure KI-Dienste bieten.

Spark bietet eine eigene Python- und R-Integration – pySpark bzw. SparkR – sowie Connectors zum Lesen und Schreiben von JSON, XML und AVRO.

Wenn Sie ein Skript transformieren müssen, das auf die Azure KI-Dienstbibliotheken verweist, empfehlen wir, uns über Ihren Microsoft-Kontomitarbeiter zu kontaktieren.

Transformieren von typisierten Werten

Da das Typsystem von U-SQL auf dem .NET-Typsystem basiert und Spark über ein eigenes Typsystem verfügt, das von der Hostsprachbindung betroffen ist, müssen Sie sicherstellen, dass die Typen, mit denen Sie arbeiten, geschlossen sind, und für bestimmte Typen können sich die Typbereiche, die Genauigkeit und/oder die Skalierung geringfügig unterscheiden. Darüber hinaus behandeln U-SQL und Spark null-Werte unterschiedlich.

Datentypen

Die folgende Tabelle zeigt die äquivalenten Typen in Spark, Scala und PySpark für die jeweiligen U-SQL-Typen.

U-SQL Spark Scala PySpark
byte
sbyte ByteType Byte ByteType
int IntegerType Int IntegerType
uint
long LongType Long LongType
ulong
float FloatType Float FloatType
double DoubleType Double DoubleType
decimal DecimalType java.math.BigDecimal DecimalType
short ShortType Short ShortType
ushort
char Char
string StringType String StringType
DateTime DateType, TimestampType java.sql.Date, java.sql.Timestamp DateType, TimestampType
bool BooleanType Boolean BooleanType
Guid
byte[] BinaryType Array[Byte] BinaryType
SQL.MAP<K,V> MapType(keyType, valueType, valueContainsNull) scala.collection.Map MapType(keyType, valueType, valueContainsNull=True)
SQL.ARRAY<T> ArrayType(elementType, containsNull) scala.collection.Seq ArrayType(elementType, containsNull=True)

Weitere Informationen finden Sie unter:

Behandlung von NULL

In Spark lassen Typen NULL-Werte standardmäßig zu, wohingegen Sie in U-SQL skalare Elemente, bei denen es sich nicht um Objekte handelt, explizit als „Nullwerte zulassend“ kennzeichnen müssen. In Spark können Sie zwar eine Spalte als „keine Nullwerte zulassend“ definieren, aber die Einschränkung wird nicht erzwungen, was zu falschen Ergebnissen führen kann.

In Spark bedeutet NULL, dass der Wert unbekannt ist. Ein NULL-Wert in Spark unterscheidet sich von jedem anderen Wert, einschließlich sich selbst. Vergleiche zwischen zwei Spark-NULL-Werten oder zwischen einem NULL- und einem anderen Wert geben „unbekannt“ zurück, weil der Wert jedes NULL-Elements unbekannt ist.

Dieses Verhalten unterscheidet sich von U-SQL – diese Sprache folgt der C#-Semantik, bei der null sich von allen anderen Werten unterscheidet und nur sich selbst entspricht.

Daher gibt in SparkSQL eine SELECT-Anweisung, die WHERE column_name = NULL verwendet, 0 Zeilen zurück, auch wenn in column_name NULL-Werte enthalten sind. In U-SQL dagegen gibt diese Anweisung die Zeilen zurück, in denen column_name auf null festgelegt ist. Ebenso gibt in Spark eine SELECT-Anweisung, die WHERE column_name != NULL verwendet, 0 Zeilen zurück, auch wenn in column_name Nicht-NULL-Werte enthalten sind. In U-SQL dagegen gibt diese Anweisung die Zeilen zurück, die Nicht-NULL-Werte enthalten. Wenn Sie die U-SQL-Semantik zur Überprüfung auf NULL verwenden möchten, müssen Sie daher isnull bzw. isnotnull (oder das jeweilige DSL-Äquivalent) verwenden.

Transformieren von U-SQL-Katalogobjekten

Ein wesentlicher Unterschied besteht darin, dass U-SQL-Skripts Katalogobjekte verwenden können, von denen viele kein direktes Äquivalent in Spark aufweisen.

Spark bietet Unterstützung für die Hive-Metaspeicherkonzepte – hauptsächlich Datenbanken, Tabellen und Sichten –, daher können Sie U-SQL-Datenbanken und -Schemas zu Hive-Datenbanken und U-SQL-Tabellen zu Spark-Tabellen zuordnen (siehe Verschieben von in U-SQL-Tabellen gespeicherten Daten). Tabellenwertfunktionen (Table-Valued Functions, TVFs), gespeicherte Prozeduren, U-SQL-Assemblys, externe Datenquellen usw. werden dagegen nicht unterstützt.

U-SQL-Codeobjekte wie Sichten, TVFs, gespeicherte Prozeduren und Assemblys können mit Codefunktionen und Bibliotheken in Spark modelliert werden. Verweise auf diese Objekte erfolgen über die Funktions- und Prozedurabstraktionsmechanismen der Hostsprache (beispielweise durch Importieren von Python-Modulen oder Verweisen auf Scala-Funktionen).

Wenn der U-SQL-Katalog zum Freigeben von Daten- und Codeobjekten für Projekte und Teams dient, müssen entsprechende Mechanismen für die Freigabe verwendet werden (z. B. Maven für Freigabe von Codeobjekten).

Transformieren von U-SQL-Rowsetausdrücken und SQL-basierten Skalarausdrücken

Die Kernsprache von U-SQL transformiert Rowsets und basiert auf SQL. Im Folgenden finden Sie eine nichtexhaustive Liste der häufigsten Rowsetausdrücke, die in U-SQL angeboten werden:

  • SELECT/FROM/WHERE/GROUP BY+Aggregates+HAVING/ORDER BY+FETCH

  • INNER/OUTER/CROSS/SEMIJOIN-Ausdrücke

  • CROSS/OUTERAPPLY-Ausdrücke

  • PIVOT/UNPIVOT-Ausdrücke

  • VALUES-Rowsetkonstruktor

  • Festgelegte Ausdrücke UNION/OUTER UNION/INTERSECT/EXCEPT

Darüber hinaus bietet U-SQL verschiedene SQL-basierte skalare Ausdrücke wie

  • OVER-Fensterausdrücke
  • verschiedene integrierte Aggregatoren und Rangfolgefunktionen (SUMusw FIRST .)
  • Einige der bekanntesten SQL-Skalarausdrücke: CASE, LIKE, (NOT) IN, AND, OR usw.

Spark bietet sowohl in der DSL- als auch in der SparkSQL-Variante Äquivalente für die meisten dieser Ausdrücke. Einige der in Spark nicht nativ unterstützten Ausdrücke müssen mithilfe einer Kombination aus den nativen Spark-Ausdrücken und den semantisch äquivalenten Mustern neu geschrieben werden. OUTER UNION beispielsweise muss in die entsprechende Kombination aus Projektionen und Vereinigungen übersetzt werden.

Aufgrund der unterschiedlichen Behandlung von NULL-Werten stimmt ein U-SQL-Join immer mit einer Zeile überein, wenn beide verglichenen Spalten einen NULL-Wert enthalten, während eine Verknüpfung in Spark nicht mit diesen Spalten übereinstimmt, es sei denn, explizite NULL-Überprüfungen werden hinzugefügt.

Transformieren anderer U-SQL-Konzepte

U-SQL bietet auch verschiedene andere Features und Konzepte, z. B. Verbundabfragen für SQL Server Datenbanken, Parameter, skalare und Lambdaausdrucksvariablen, Systemvariablen und OPTION Hinweise.

Verbundabfragen in SQL Server-Datenbanken bzw. externen Tabellen

U-SQL stellt Datenquellen und externe Tabellen bereit und ermöglicht direkte Abfragen von Azure SQL-Datenbank. Spark bietet zwar nicht die gleichen Objektabstraktionen, stellt jedoch den Spark-Connector für Azure SQL-Datenbank bereit, der zum Abfragen von SQL-Datenbanken verwendet werden kann.

U-SQL-Parameter und -Variablen

Parameter und Benutzervariablen weisen in Spark und den zugehörigen Hostsprachen die gleichen Konzepte auf.

In Scala können Sie beispielsweise eine Variable mit dem Schlüsselwort var definieren:

var x = 2 * 3;
println(x)

Die Systemvariablen von U-SQL (Variablen, die mit @@ beginnen) können in zwei Kategorien unterteilt werden:

  • Festlegbare Systemvariablen, die auf bestimmte Werte festgelegt werden können, um das Skriptverhalten zu beeinflussen
  • Informative Systemvariablen, die Informationen auf System- und Auftragsebene abfragen

Die meisten festlegbaren Systemvariablen weisen kein direktes Äquivalent in Spark auf. Einige der informativen Systemvariablen können durch Übergeben der Informationen als Argumente bei der Auftragsausführung modelliert werden, andere weisen eine äquivalente Funktion in der Hostsprache von Spark auf.

U-SQL-Hinweise

U-SQL bietet verschiedene syntaktische Möglichkeiten, Hinweise für den Abfrageoptimierer und die Ausführungs-Engine bereitzustellen:

  • Festlegen einer U-SQL-Systemvariable
  • Eine OPTION-Klausel, die dem Rowsetausdruck zugeordnet ist, um einen Daten- oder Planhinweis zu liefern
  • Ein Joinhinweis in der Syntax des Joinausdrucks (z. B. BROADCASTLEFT)

Der kostenbasierte Abfrageoptimierer von Spark bietet eigene Funktionen zum Bereitstellen von Hinweisen und Optimieren der Abfrageleistung. Weitere Informationen finden Sie in der entsprechenden Dokumentation.

Nächste Schritte