Entwerfen von Python-UDFs

Unter diesem Thema finden Sie Informationen zum Entwerfen von Python-UDFs.

Unter diesem Thema:

Bemerkung

Mit vektorisierten Python-UDFs können Sie Python-Funktionen definieren, mit denen Batches von Eingabezeilen als Pandas DataFrames empfangen und Batches von Ergebnissen als Pandas-Arrays oder Pandas Series zurückgeben werden. Die Batchschnittstelle führt zu einer wesentlichen Leistungssteigerung bei Szenarios mit Machine Learning-Inferenz. Weitere Informationen dazu finden Sie unter Vektorisierte Python-UDFs.

Auswählen Ihrer Datentypen

Bevor Sie Ihren Code schreiben:

  • Wählen Sie die Datentypen aus, die Ihre Funktion als Argumente akzeptieren soll, und den Datentyp, den Ihre Funktion zurückgeben soll.

  • Berücksichtigen Sie die Zeitzonenproblematik.

  • Entscheiden Sie, wie NULL-Werte behandelt werden sollen.

Weitere Informationen zur Zuordnung von Python-Datentypen zu SQL-Datentypen in Snowflake finden Sie unter Zuordnung von Datentypen zwischen SQL und Python.

TIMESTAMP_LTZ-Werte und Zeitzonen

Eine Python-UDF ist weitgehend isoliert von der Umgebung, in der sie aufgerufen wird. Die Zeitzone wird jedoch von der aufrufenden Umgebung geerbt. Wenn für die Sitzung des Aufrufers vor dem Aufruf der Python-UDF eine Standardzeitzone eingestellt wurde, dann hat die Python-UDF die gleiche Standardzeitzone. Weitere Informationen zu Zeitzonen finden Sie unter TIMEZONE.

NULL-Werte

Für alle Snowflake-Typen mit Ausnahme von Variant wird ein SQL-NULL-Argument einer Python-UDF in den Python-Wert None konvertiert, und ein zurückgegebener Python-Wert None wird zurück in SQL-NULL konvertiert.

Ein Wert vom Typ Variante kann entweder ein SQL-NULL- oder ein VARIANT-JSON-null-Wert sein. Weitere Informationen zu Snowflake-VARIANT-NULL finden Sie unter NULL-Werte.

  • Ein VARIANT-JSON null-Wert wird in Python-None konvertiert.

  • Ein SQL-NULL-Wert wird in ein Python-Objekt übersetzt, das das Attribut is_sql_null hat.

Ein Beispiel dazu finden Sie unter NULL-Verarbeitung in Python-UDFs.

Entwerfen von Python-UDFs unter Berücksichtigung der Snowflake-bedingten Einschränkungen

Um die Stabilität in der Snowflake-Umgebung sicherzustellen, hat Snowflake die folgenden Einschränkungen für Python-UDFs definiert. Wenn nicht anders angegeben, werden diese Einschränkungen bei der Ausführung der Java-UDF durchgesetzt, nicht bei der Erstellung.

Das Training von Modellen des maschinellen Lernens (ML) kann manchmal sehr ressourcenintensiv sein. Snowpark-optimierte Warehouses sind ein Typ von virtuellen Snowflake-Warehouse, der für Workloads verwendet werden kann, die eine große Menge an Arbeitsspeicher und Computeressourcen benötigen. Weiter Informationen zu Machine Learning-Modellen und zu Snowpark Python finden Sie unter Training von Machine Learning-Modellen mit Snowpark Python.

Speicher

Vermeiden Sie einen zu hohen Verbrauch von Speicher.

  • Große Datenwerte können eine große Menge an Arbeitsspeicher verbrauchen.

  • Eine zu große Stacktiefe kann eine große Menge an Arbeitsspeicher verbrauchen.

UDFs geben einen Fehler zurück, wenn sie zu viel Speicher verbrauchen. Das jeweilige Limit kann sich ändern.

Wenn UDFs nicht funktioniert, weil sie zu viel Arbeitsspeicher verbrauchen, sollten Sie die Verwendung von Snowpark-optimierte Warehouses erwägen.

Dauer

Vermeiden Sie Algorithmen, die pro Aufruf sehr viel Zeit benötigen.

Wenn eine UDF-Anweisung zu lange dauert, bricht Snowflake die SQL-Anweisung ab und gibt einen Fehler an den Benutzer zurück. Dieses Limit begrenzt die Auswirkungen (und Kosten) von Fehlern wie Endlosschleifen.

Entwerfen des Moduls

Wenn eine SQL-Anweisung Ihre Python-UDF aufruft, ruft Snowflake eine von Ihnen geschriebene Python-Funktion auf. Ihre Python-Funktion wird als „Handler-Funktion“ oder kurz „Handler“ bezeichnet. Der Handler ist eine Funktion, die in einem vom Benutzer bereitgestellten Modul implementiert ist.

Wie bei jeder Python-Funktion muss auch Ihre Funktion als Teil eines Moduls deklariert werden.

Der Handler wird für jede an die Python-UDF übergebene Zeile einmal aufgerufen. Das Modul, das die Funktion enthält, wird nicht für jede Zeile neu importiert. Snowflake kann die Handler-Funktion desselben Moduls mehr als einmal aufrufen.

Um die Ausführung Ihres Codes zu optimieren, geht Snowflake davon aus, dass die Initialisierung langsam sein kann, während die Ausführung der Handler-Funktion schnell ist. Snowflake verwendet ein längeres Timeout für die Ausführung der Initialisierung (einschließlich Zeit für Laden der UDF und zum Initialisieren des Moduls) als für die Ausführung des Handlers (Zeit für Aufruf Ihres Handlers mit einer Eingabezeile).

Weitere Informationen zum Entwerfen des Moduls finden Sie unter Erstellen von Python-UDFs.

Optimieren von Initialisierung und Steuerung des globalen Zustands in skalaren UDFs

Die meisten skalaren UDFs müssen den folgenden Richtlinien folgen:

  • Wenn Sie einen gemeinsamen Zustand (Shared State) initialisieren müssen, der sich über Zeilen hinweg nicht ändert, initialisieren Sie ihn nicht in der Handler-Funktion sondern im Modul.

  • Schreiben Sie Ihre Handler-Funktion so, dass sie threadsicher ist.

  • Vermeiden Sie das Speichern und Freigeben eines dynamischen Zustands über Zeilen hinweg.

Wenn Ihre UDF diese Richtlinien nicht erfüllen kann, müssen Sie beachten, dass Snowflake erwartet, dass skalare UDFs unabhängig verarbeitet werden. Wenn Sie sich zwischen Aufrufen auf einen gemeinsamen Zustand verlassen, kann dies zu unerwartetem Verhalten führen, da das System Zeilen in beliebiger Reihenfolge verarbeiten und diese Aufrufe auf mehrere Instanzen verteilen kann. Darüber hinaus kann dieselbe Handler-Funktion innerhalb desselben Python-Interpreters auf mehreren Threads mehrfach ausgeführt werden.

Bei UDFs sollte es vermieden werden, dass sich beim Aufrufen der Handler-Funktion auf einen gemeinsamen Zustand verlassen wird. Es gibt jedoch zwei Situationen, in denen Sie möglicherweise möchten, dass eine UDF einen gemeinsamen Zustand speichert:

  • Code, der teure Initialisierungslogik enthält, die nicht bei jeder Zeile wiederholt werden soll.

  • Code, der einen gemeinsamen Zustand über Zeilen hinweg nutzt, z. B. als Cache.

Wenn es notwendig ist, einen globalen Zustand zu erhalten, der über Handler-Aufrufe hinweg gemeinsam genutzt wird, müssen Sie den globalen Zustand gegen eine Data Race-Situation (Wettlaufsituation) schützen, indem Sie die unter Threading – Thread-basierte Parallelität beschriebenen Synchronisierungsprimitive verwenden.

Optimierung für Skalierung und Performance

Vektorisierte Python-UDFs mit Data Science-Bibliotheken verwenden

Wenn Ihr Code Machine Learning- oder Data Science-Bibliotheken verwendet, verwenden Sie vektorisierte Python-UDFs, um Python-Funktionen zu definieren, die Eingabezeilen in Batches empfangen, für die diese Bibliotheken optimiert sind.

Weitere Informationen dazu finden Sie unter Vektorisierte Python-UDFs.

Single-Thread-UDF-Handler schreiben

Schreiben Sie UDF-Handler, die mit einem einzigen Thread arbeiten. Snowflake übernimmt die Partitionierung der Daten und die Skalierung der UDF auf die Computeressourcen des virtuellen Warehouses.

Teure Initialisierung über das Modul ausführen

Legen Sie aufwändigen Initialisierungscode in den Modulbereich. Dort wird er einmalig bei der Initialisierung der UDF durchgeführt. Vermeiden Sie die Wiederholung des teuren Initialisierungscodes bei jedem Aufruf eines UDF-Handlers.

Fehlerbehandlung

Eine Python-Funktion, die als UDF verwendet wird, kann die normalen Python-Ausnahmebehandlungsverfahren verwenden, um Fehler innerhalb der Funktion abzufangen.

Wenn eine Ausnahme innerhalb der Funktion auftritt und nicht von der Funktion abgefangen wird, gibt Snowflake einen Fehler aus, der den Stacktrace für die Ausnahme enthält. Wenn die Protokollierung von unbehandelten Ausnahmen aktiviert ist, protokolliert Snowflake Daten zu unbehandelten Ausnahmen in einer Ereignistabelle.

Sie können explizit einen Fehler auszulösen, ohne ihn abzufangen, um die Abfrage zu beenden und einen SQL-Fehler zu erzeugen. Beispiel:

if (x < 0):
  raise ValueError("x must be non-negative.");
Copy

Beim Debugging können Sie in den Text der SQL-Fehlermeldung Werte integrieren. Setzen Sie dazu den gesamten Textkörper der Python-Methode in einen „try-catch“-Block, und fügen Sie Argumentwerte zur abgefangenen Fehlermeldung hinzu. Lösen Sie dann eine Ausnahme mit der erweiterten Meldung aus. Um die Offenlegung sensibler Daten zu verhindern, entfernen Sie die Argumente vor der Bereitstellung in der Produktionsumgebung.

Einsetzen von bewährten Sicherheitsmethoden

Damit Ihr Handler auf sichere Weise funktioniert, beachten Sie die unter Sicherheitsverfahren für UDFs und Prozeduren beschriebenen Best Practices.