네이티브 실행 엔진의 Python UDF, Scala UDF 및 복합 데이터 형식

이제 Microsoft Fabric 네이티브 실행 엔진은 UDF(사용자 정의 함수), Scala UDF 및 복잡한 데이터 형식(배열, 맵 및 구조체)Python 지원합니다. 이러한 기능을 사용하면 성능을 저하시키지 않고 표현형 Spark 애플리케이션을 작성할 수 있습니다.

Python UDF 지원

Python 데이터 엔지니어링 및 데이터 과학에서 가장 인기 있는 언어 중 하나입니다. 지금까지 Python UDF는 JVM과 Python 작업자 프로세스 간의 직렬화 비용으로 인해 Spark에 상당한 오버헤드를 발생시켰습니다. 네이티브 실행 엔진은 이러한 비용이 많이 드는 전환을 최소화하여 코드 변경 없이 더 빠르게 실행할 수 있도록 합니다.

Python UDF가 네이티브 실행 엔진에서 작동하는 방법

기존 Spark 실행 모델에서 Python UDF 실행에는 다음이 포함됩니다.

  1. Spark의 내부 형식에서 데이터 변환
  2. Python 작업자 프로세스로의 직렬화 및 전송
  3. Python UDF 실행
  4. 결과를 JVM으로 다시 serialization합니다.
  5. Spark가 실행을 다시 시작합니다.

이 런타임 간 이동은 직렬화/역직렬화 비용, CPU 비효율성, 그리고 손상된 컬럼형 실행 파이프라인을 초래합니다. 네이티브 실행 엔진은 데이터 전송 경로를 최적화하고 가능한 경우 벡터화된 처리를 유지 관리하여 이 오버헤드를 줄입니다.

지원되는 Python UDF 형식

네이티브 실행 엔진은 다음을 지원합니다.

  • Scalar UDF: udf() 등록된 행별 Python 함수입니다.
  • 벡터화된(Pandas) UDF: 효율적인 전송을 위해 Apache Arrow를 사용하여 데이터 일괄 처리에서 작동하는 데코레이팅된 @pandas_udf 함수입니다.

벡터화된 UDF는 네이티브 실행 엔진의 열 형식 처리 모델에 자연스럽게 맞춰지므로 가장 큰 성능 향상을 보입니다.

예: 벡터화된 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()

네이티브 실행 엔진을 사용하도록 설정하는 것 외에는 추가 구성이 필요하지 않습니다. 기존 Python UDF는 자동으로 혜택을 누릴 수 있습니다.

Scala UDF 지원

네이티브 실행 엔진은 Scala UDF도 가속화합니다. Scala UDF는 JVM에서 기본적으로 실행되므로 엔진은 지원되는 작업을 벡터화된 C++ 실행 경로로 오프로드하면서 Scala UDF 평가를 동일한 런타임 내에 효율적으로 유지할 수 있습니다.

예: 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는 네이티브 실행 엔진을 사용할 때 코드 변경 없이 가속화됩니다.

복잡한 데이터 형식 지원

최신 레이크하우스 아키텍처는 반구조적 데이터와 중첩된 데이터에 따라 달라집니다. 네이티브 실행 엔진은 이제 다음에 대해 최적화된 지원을 제공합니다.

데이터 형식 Description 예제 사용 사례
배열 정렬된 요소 컬렉션 이벤트 태그, 제품 범주
지도 키-값 쌍 구성 속성, 메타데이터
구조체 형식이 다른 명명된 필드 중첩된 고객 레코드, 주소 개체

복합 형식에서 지원되는 연산

네이티브 실행 엔진은 복잡한 데이터 형식에 대한 일반적인 작업을 가속화합니다.

  • 배열 함수: explode, array_contains, size, flattentransform
  • Map 함수: map_keys, map_valueselement_at
  • 구조체 액세스: 점 표기법 필드 접근, getField
  • 중첩된 조합: 구조체 배열, 배열 값이 있는 맵

예: 배열 및 구조체 작업

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

예제: 맵 작업

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

성능 결과

내부 벤치마킹은 Python UDF 및 복잡한 데이터 형식을 사용하는 워크로드에서 크게 향상된 기능을 보여줍니다.

워크로드 유형 성능 향상
벡터화된 Python UDF 최대 5.76배 더 빠름
스칼라 Python UDF들 최대 1.08배 더 빠릅니다.
TPC-DS 엔드투엔드(복합 타입 포함) 최대 2.35배 더 빠름

이러한 성능 향상은 직렬화 오버헤드 감소, 벡터화 개선, 그리고 엔드 투 엔드 컬럼형 실행의 결과입니다.

고급 레이크하우스 패턴의 이점

복잡한 데이터 형식 가속은 다음 경우에 특히 중요합니다.

  • Z-ORDER 최적화: 중첩 열은 최적화된 데이터 레이아웃에 참여합니다.
  • 액체 클러스터링: 복합 형식 열은 평면화하지 않고 클러스터링의 이점을 누릴 수 있습니다.
  • 반구조화된 분석: JSON 페이로드 및 이벤트 스트림은 자연 쿼리를 위해 중첩된 상태로 유지됩니다.
  • 이벤트 기반 아키텍처: 원격 분석 및 IoT 데이터는 계층 구조를 유지합니다.

성능을 위해 데이터를 평면화하거나 파이프라인을 재구성하는 대신, 높은 실행 효율성을 유지하면서 복잡한 스키마를 자연스럽게 사용합니다.

기능을 활성화하세요.

Python UDF, Scala UDF 및 복잡한 데이터 형식 지원은 네이티브 실행 엔진을 사용하는 경우 사용할 수 있습니다. 추가 구성은 필요하지 않습니다.

네이티브 실행 엔진을 사용하도록 설정하려면 패브릭 데이터 엔지니어링에 대한 네이티브 실행 엔진을 참조하세요.

사전 요구 사항

Limitations

  • 모든 Python 라이브러리가 벡터화된 경로 내에서 지원되는 것은 아닙니다. 임의의 Python 객체 직렬화가 필요한 라이브러리는 여전히 폴백을 유발할 수 있습니다.
  • 깊이 중첩된 복합 형식(예: 구조체 맵의 배열)은 특정 작업을 위해 JVM 엔진으로 대체될 수 있습니다.
  • ANSI 모드는 네이티브 실행 엔진에서 지원되지 않습니다.