Python UDF-er, Scala UDF-er og komplekse datatyper i den native utførelsesmotoren

Den innebygde kjøremotoren i Microsoft Fabric støtter nå Python brukerdefinerte funksjoner (UDF), Scala UDF-er og komplekse datatyper (arrays, kart og structs). Disse mulighetene lar deg skrive uttrykksfulle Spark-applikasjoner uten å ofre ytelsen.

Python UDF-støtte

Python er et av de mest populære språkene innen data engineering og data science. Historisk sett har Python UDF-er introdusert betydelig overhead i Spark på grunn av serialiseringskostnader mellom JVM- og Python-arbeidsprosessene. Den innebygde utførelsesmotoren minimerer disse kostbare overgangene, noe som muliggjør raskere utførelse uten kodeendringer.

Hvordan Python UDF-er fungerer i en native kjøringsmotor

I en konvensjonell Spark-kjøringsmodell innebærer Python UDF-utførelse:

  1. Datakonvertering fra Sparks interne format.
  2. Serialisering og overføring til Python-arbeidsprosesser.
  3. Python UDF-utførelse.
  4. Serialisering av resultatene tilbake til JVM.
  5. Spark gjenopptar henrettelsen.

Denne tverrkjøringsbevegelsen skaper serialiserings-/deserialiseringskostnader, CPU-ineffektivitet og ødelagte kolonnekjøringspipelines. Den innebygde utførelsesmotoren reduserer denne overheaden ved å optimalisere dataoverføringsbanen og opprettholde vektorisert prosessering der det er mulig.

Støttede Python UDF-typer

Den innebygde kjøremotoren støtter:

  • Scalar UDFs: Rad for rad Python funksjoner registrert med udf().
  • Vektoriserte (Pandas) UDF-er: Funksjoner dekorert med @pandas_udf som opererer på databatcher ved hjelp av Apache Arrow for effektiv overføring.

Vektoriserte UDF-er oppnår størst ytelsesgevinst fordi de naturlig samsvarer med kolonneprosesseringsmodellen til den native kjøringsmotoren.

Eksempel: Vektorisert Python UDF

import pandas as pd
from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import DoubleType

@pandas_udf(DoubleType())
def calculate_discount(price: pd.Series, rate: pd.Series) -> pd.Series:
    return price * (1 - rate)

df = spark.table("sales.transactions")
result = df.withColumn("discounted_price", calculate_discount(df.price, df.discount_rate))
result.show()

Ingen ekstra konfigurasjon kreves utover å aktivere den native kjøringsmotoren. Eksisterende Python UDF-er drar automatisk nytte av det.

Scala UDF-støtte

Den innebygde kjøremotoren akselererer også Scala UDF-er. Fordi Scala UDF-er kjører nativt i JVM, kan motoren overføre støttede operasjoner til den vektoriserte C++-kjøringsveien, samtidig som evalueringen av Scala UDF holdes effektiv innenfor samme kjøretid.

Eksempel: Scala UDF

import org.apache.spark.sql.functions.udf

val toUpperCase = udf((s: String) => s.toUpperCase)
val df = spark.table("catalog.customers")
val result = df.withColumn("name_upper", toUpperCase(df("name")))
result.show()

Scala UDF-er som opererer på støttede datatyper akselereres uten kodeendringer når den native kjøringsmotoren er aktivert.

Støtte for komplekse datatyper

Moderne innsjøhusarkitekturer er avhengige av semistrukturerte og nestede data. Den innebygde utførelsesmotoren gir nå optimalisert støtte for:

Datatype Beskrivelse Eksempel på brukstilfelle
Matrise Ordnet samling av elementer Hendelsestagger, produktkategorier
Kart Nøkkelverdipar Konfigurasjonsegenskaper, metadata
Struct Navngitte felt med ulike typer Nestede kundeposter, adresseobjekter

Operasjoner støttet for komplekse typer

Den innebygde utførelsesmotoren akselererer vanlige operasjoner på komplekse datatyper:

  • Arrayfunksjoner: explode, array_contains, , size, flatten, transform
  • Avbildningsfunksjoner: map_keys, map_values, element_at
  • Struct-tilgang: Punktnotasjonsfelt-tilgang, getField
  • Nestede kombinasjoner: Arrays av strukturer, kart med array-verdier

Eksempel: Arbeid med arrays og strukturer

from pyspark.sql.functions import explode, col, size

# Read data with nested schema
df = spark.table("events.telemetry")

# Operations on arrays - accelerated by native engine
result = (df
    .filter(size(col("tags")) > 0)
    .select(
        col("event_id"),
        col("metadata.source"),  # Struct field access
        explode(col("tags")).alias("tag")
    )
)
result.show()

Eksempel: Arbeid med kart

from pyspark.sql.functions import map_keys, map_values, col

df = spark.table("config.settings")

# Map operations - accelerated by native engine
result = (df
    .select(
        col("setting_id"),
        map_keys(col("properties")).alias("keys"),
        map_values(col("properties")).alias("values")
    )
)
result.show()

Ytelsesresultater

Intern benchmarking viser betydelige forbedringer på tvers av arbeidsbelastninger som bruker Python UDF-er og komplekse datatyper:

Arbeidsbelastningstype Forbedring av ytelsen
Vektoriserte Python UDF-er Opptil 5,76 ganger raskere
Skalar Python UDF-er Opptil 1,08 ganger raskere
TPC-DS ende-til-ende (med komplekse typer) Opptil 2,35 ganger raskere

Disse gevinstene skyldes redusert serialiseringsoverhead, forbedret vektorisering og ende-til-ende kolonneutførelse.

Fordeler med avanserte lakehouse-mønstre

Akselerasjon av komplekse datatyper er spesielt viktig for:

  • Z-ORDER-optimalisering: Nestede kolonner deltar i optimalisert dataoppsett.
  • Væskeklynging: Komplekse type kolonner drar nytte av klynging uten utflating.
  • Semistrukturert analyse: JSON-nyttelaster og hendelsesstrømmer forblir nestet for naturlig spørring.
  • Hendelsesdrevne arkitekturer: Telemetri og IoT-data beholder sin hierarkiske struktur.

I stedet for å flate ut data eller omstrukturere pipelines for ytelse, bør du jobbe naturlig med komplekse skjemaer samtidig som du opprettholder høy utførelseseffektivitet.

Aktivere funksjonen

Python UDF, Scala UDF og støtte for komplekse datatyper er tilgjengelig når den native kjøringsmotoren er aktivert. Ingen ekstra konfigurasjon er nødvendig.

For å aktivere den innebygde kjøringsmotoren, se Native utførelsesmotor for Fabric Data Engineering.

Forutsetninger

Begrensninger

  • Ikke alle Python-biblioteker støttes innenfor den vektoriserte banen. Biblioteker som krever vilkårlig Python-objektserialisering kan fortsatt utløse fallback.
  • Dypt nestede komplekse typer (for eksempel arrays av kart over strukturer) kan falle tilbake til JVM-motoren for visse operasjoner.
  • ANSI-modus støttes ikke med den native kjøringsmotoren.