서버리스 컴퓨트에서 DataFrame 캐시하기

Important

이 기능은 공개 미리보기 단계에 있습니다. 워크스페이스 관리자는 Previews 페이지에서 서버리스 컴퓨트에서 DataFrame 캐싱 접근을 제어할 수 있습니다. Azure Databricks 미리 보기 관리를 참조하세요.

서버리스 컴퓨트에서는 세션에서 비용이 많이 드는 결과를 재사용하기 위해 df.cache() 또는 df.persist()로 DataFrame을 캐시할 수 있습니다. Azure Databricks는 호출 cache() 시 DataFrame을 계산하고 결과를 스냅샷으로 실체화합니다. 데이터프레임과 그로부터 생성한 데이터프레임들은 세션의 나머지 기간 동안 다시 계산하는 대신 스냅샷을 읽습니다.

Requirements

  • 서버리스 컴퓨트의 DataFrame 캐싱 미리보기는 작업 공간에서 활성화되어 있습니다(기본적으로 활성화됨). 워크스페이스 관리자는 Previews 페이지에서 비활성화할 수 있습니다. 컴플라이언스 보안 프로필이 활성화된 작업 공간에서는 미리보기를 사용할 수 없습니다. 작업 영역 수준 미리 보기 관리를 참조하세요.
  • 서버리스 컴퓨트의 노트북과 작업은 환경 버전 5 이상을 사용해야 합니다.
  • Databricks Connect for Python은 18.1.8 이상이어야 하고, Databricks Connect for Scala는 18.1.5 이상이어야 합니다.

구버전 클라이언트는 CACHE_REQUIRES_NEWER_CLIENT_ON_SERVERLESS_GC 오류가 발생합니다.

서버리스 컴퓨트에서 DataFrame 캐싱을 사용해야 하는 경우

서버리스 컴퓨트에서 DataFrame을 캐시해야 할 때:

  • 비싼 조인, 집계, 필터 결과를 같은 세션에서 여러 번 재사용할 수 있습니다.
  • foreachBatch 함수에서 스트리밍 마이크로 배치를 한 번 계산한 뒤, 여러 싱크에 쓰는 식입니다.
  • 동일한 준비된 특징 데이터를 기반으로 여러 머신러닝 모델을 학습하거나 조정합니다.
  • 소스 테이블이 바뀌더라도 여러 단계의 분석을 실행할 때 안정적인 스냅샷을 유지하세요.

모범 사례를 참조하세요.

서버리스 컴퓨트에서 DataFrame API 캐시

다음 예시는 고객 데이터와 연결된 하루의 주문을 캐시하고, 그날의 총 수익을 계산한 뒤, 지역별 수익을 보고서 테이블에 추가한 후 캐시를 해제합니다. 두 결과 모두 조인을 다시 실행하지 않고 캐시된 스냅샷을 읽었습니다.

Python

from pyspark.sql import functions as F

# The DataFrame to cache: one day of orders joined with customer data.
oct_15_orders = (
    spark.table("main.sales.orders")
    .filter(F.col("order_date") == "2026-10-15")
    .join(spark.table("main.sales.customers"), "customer_id")
)
oct_15_orders.cache()

# Both results are built from oct_15_orders, so they read the cached snapshot.
total_revenue = oct_15_orders.agg(F.sum("amount")).first()[0]
print(f"Total revenue on 2026-10-15: {total_revenue}")

revenue_by_region = oct_15_orders.groupBy("order_date", "region").agg(F.sum("amount").alias("revenue"))
revenue_by_region.write.mode("append").saveAsTable("main.reports.revenue_by_region")

# Release the cache and the DataFrame when you no longer need it.
# oct_15_orders and DataFrames built from it can no longer be used.
oct_15_orders.unpersist()

Scala

import org.apache.spark.sql.functions._

// The DataFrame to cache: one day of orders joined with customer data.
val oct15Orders = spark.table("main.sales.orders")
  .filter(col("order_date") === "2026-10-15")
  .join(spark.table("main.sales.customers"), "customer_id")
oct15Orders.cache()

// Both results are built from oct15Orders, so they read the cached snapshot.
val totalRevenue = oct15Orders.agg(sum("amount")).first().get(0)
println(s"Total revenue on 2026-10-15: $totalRevenue")

val revenueByRegion = oct15Orders.groupBy("order_date", "region").agg(sum("amount").alias("revenue"))
revenueByRegion.write.mode("append").saveAsTable("main.reports.revenue_by_region")

// Release the cache and the DataFrame when you no longer need it.
// oct15Orders and DataFrames built from it can no longer be used.
oct15Orders.unpersist()

다음 API 설명에서 df는 앞선 예시의 oct_15_orders와 같은 세션 내 임의의 DataFrame을 의미합니다.

df.cache()

df를 즉시 계산하고 결과를 남은 세션 동안 스냅샷으로 캐시합니다.

  • 반환: 동일한 캐시된 데이터프레임 df, 이제 소스 테이블 계산 대신 스냅샷을 기반으로 합니다. 다음 동작을 참고하세요.
  • 행동:
    • DataFrame의 쿼리를 즉시 실행하고, 호출이 반환되기 전에 결과를 스냅샷으로 실체화합니다.
    • 호출 df 후 반환되는 데이터프레임(df 자체)은 스냅샷을 기반으로 합니다. 이들은 더 이상 소스 테이블의 계산과 연결되어 있지 않기 때문에, 쿼리를 다시 계산하는 대신 스냅샷을 읽습니다.
    • df에서 직접 또는 다른 파생 데이터프레임을 통해 파생된 데이터프레임은 원래 쿼리를 다시 실행하는 대신 스냅샷을 자동으로 읽습니다. 스냅샷 읽기를 참조하세요.
    • 스냅샷은 현재 세션에만 적용됩니다. df을 캐시한 세션만 이를 읽을 수 있습니다. 격리 및 권한을 참조하세요.
    • 이 스냅샷은 세션이 끝날 때까지 최대 7일간 지속됩니다. 그 후 Azure Databricks는 백그라운드에서 자동으로 저장소를 회수합니다. df.unpersist()를 호출하면 해당 항목이 저장소를 더 일찍 회수할 수 있습니다. 수명 주기와 시간 제한, 보관 및 정리를 참조하세요.
    • df 계산에 실패하면 df.cache()이 오류를 발생시키고 df는 캐시되지 않습니다.
    • 이미 캐시된 DataFrame에 df.cache()를 호출해도 아무런 효과가 없습니다. df 다시 계산되지 않습니다.

스냅샷은 df.cache()를 호출한 후 고정됩니다. 이후 df가 읽는 테이블에 기록되는 데이터와 열 추가 또는 이름 변경과 같은 해당 테이블의 스키마 변경은 이에 영향을 주지 않습니다. 최신 데이터로 재계산하려면 소스 테이블에서 DataFrame을 다시 빌드하고, 필요하다면 새 DataFrame을 캐시하세요.

스냅샷을 읽어보세요

서버리스 컴퓨트에서는 캐시 매칭이 데이터프레임 도출에 기반합니다. df.cache() 후 다음 데이터프레임만 소스 테이블에서 쿼리를 재계산하는 대신 스냅샷을 자동으로 읽습니다:

  • 사용자가 캐시한 데이터프레임, df.
  • 예를 들어 filter(), groupBy(), join() 또는 df 같은 select()에서 파생된 데이터프레임.
  • 이 데이터프레임들은 파생된 데이터프레임으로부터 추가로 체인된 데이터프레임입니다.

같은 쿼리를 반복하는 새로운 DataFrame은 로직이 동일하지만 소스 테이블을 읽습니다. 왜냐하면 이 데이터프레임은 df에서 파생되지 않기 때문입니다.

이로 인해 캐시 매치는 명시적이고 사용자의 제어 하에 있습니다: 데이터프레임은 캐시된 데이터를 캐시된 데이터프레임에서 파생할 때만 읽습니다. 하나 걸러 모든 DataFrame은 소스 테이블을 읽습니다.

Python

oct_15_orders = (
    spark.table("main.sales.orders")
    .filter(F.col("order_date") == "2026-10-15")
    .join(spark.table("main.sales.customers"), "customer_id")
)
# The join runs one time, when you call cache().
oct_15_orders.cache()

# Derived from oct_15_orders: read the snapshot, so the join doesn't run again.
large_orders = oct_15_orders.filter(F.col("amount") > 1000)
revenue_by_region = oct_15_orders.groupBy("order_date", "region").agg(F.sum("amount").alias("revenue"))

# Derived from large_orders, which is derived from oct_15_orders: also reads the snapshot.
large_orders_by_region = large_orders.groupBy("region").count()

# Not derived from oct_15_orders: reads the source tables and runs the join again.
same_query = (
    spark.table("main.sales.orders")
    .filter(F.col("order_date") == "2026-10-15")
    .join(spark.table("main.sales.customers"), "customer_id")
)

Scala

val oct15Orders = spark.table("main.sales.orders")
  .filter(col("order_date") === "2026-10-15")
  .join(spark.table("main.sales.customers"), "customer_id")
// The join runs one time, when you call cache().
oct15Orders.cache()

// Derived from oct15Orders: read the snapshot, so the join doesn't run again.
val largeOrders = oct15Orders.filter(col("amount") > 1000)
val revenueByRegion = oct15Orders.groupBy("order_date", "region").agg(sum("amount").alias("revenue"))

// Derived from largeOrders, which is derived from oct15Orders: also reads the snapshot.
val largeOrdersByRegion = largeOrders.groupBy("region").count()

// Not derived from oct15Orders: reads the source tables and runs the join again.
val sameQuery = spark.table("main.sales.orders")
  .filter(col("order_date") === "2026-10-15")
  .join(spark.table("main.sales.customers"), "customer_id")

df는 기반으로 구축된 DataFrame 대신 스냅샷을 읽기 때문에, 해당 DataFrame을 통해 df의 열을 참조할 수 없습니다. 제한 사항을 참조하세요.

df.persist(storageLevel)

저장 수준을 허용하는 df.cache()의 별칭입니다.

  • 매개 변수:
    • storageLevel (선택 사항): StorageLevel.DISK_ONLY와 같은 StorageLevel. 서버리스 컴퓨트는 어떤 저장 공간이든 허용하고 이를 무시합니다. 스냅샷은 모든 저장 수준에 대해 동일한 방식으로 저장되며, df.storageLevel는 StorageLevel.DISK_ONLY를 반환합니다.

반환 값과 동작은 df.cache()와 동일합니다.

df.unpersist(blocking)

데이터프레임의 df 캐시된 스냅샷을 해제하고 파기합니다.

  • 매개 변수:
    • blocking (선택 사항): 무시됨. df.unpersist()는 어느 값에 대해서도 즉시 반환됩니다.
  • 반환: 동일한 데이터프레임, df이제 삭제된 상태.
  • 동작:
    • 스냅샷을 회수할 수 있도록 준비됨으로 표시합니다. Azure Databricks는 캐시된 데이터를 삭제하고 백그라운드에서 저장소를 다시 회수합니다.
    • df을(를) 파괴합니다: df 및 이로부터 이전에 파생된 모든 DataFrames는 더 이상 사용할 수 없으며, 이에 대한 후속 쿼리는 CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE를 발생시킵니다. df를 호출하기 전에 df.unpersist()에서 파생된 DataFrame을 캐시하면, 그 DataFrame은 자체 스냅샷을 가지고 있기 때문에 계속 사용 가능합니다.
    • 같은 데이터 소스를 계속 사용하려면 캐시되지 않은 데이터프레임을 재사용하는 대신 원래 데이터 소스에서 새 데이터프레임을 구축하세요.
    • 캐시되지 않았거나 이미 캐시가 해제된 DataFrame에 df.unpersist()를 호출하는 것은 no-op입니다.

캐시된 데이터가 더 이상 필요하지 않을 때, 예를 들어 노트북이나 작업에서 이후 단계에서 이를 읽지 않을 때 df.unpersist() 호출하세요. 이로 인해 Azure Databricks가 더 일찍 저장소를 되찾을 수 있습니다. 데이터를 새로 고치는 용도로 df.unpersist()를 사용하지 마세요. 최신 데이터를 사용하려면 소스 테이블에서 새로운 DataFrame을 만드세요.

Python

oct_15_orders = spark.table("main.sales.orders").filter(F.col("order_date") == "2026-10-15")
oct_15_orders.cache()
large_orders = oct_15_orders.filter(F.col("amount") > 1000)
orders_per_customer = oct_15_orders.groupBy("customer_id").count()
orders_per_customer.cache()  # Has its own snapshot.

# Acknowledge that the cached data is no longer needed.
# This releases the storage and destroys oct_15_orders.
oct_15_orders.unpersist()

oct_15_orders.count()  # Raises CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE.
large_orders.count()  # Raises CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE.
orders_per_customer.count()  # Reads its own snapshot.

# If you need the data again later, build a new DataFrame from the source tables.
oct_15_orders = spark.table("main.sales.orders").filter(F.col("order_date") == "2026-10-15")

Scala

var oct15Orders = spark.table("main.sales.orders").filter(col("order_date") === "2026-10-15")
oct15Orders.cache()
val largeOrders = oct15Orders.filter(col("amount") > 1000)
val ordersPerCustomer = oct15Orders.groupBy("customer_id").count()
ordersPerCustomer.cache() // Has its own snapshot.

// Acknowledge that the cached data is no longer needed.
// This releases the storage and destroys oct15Orders.
oct15Orders.unpersist()

oct15Orders.count() // Throws CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE.
largeOrders.count() // Throws CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE.
ordersPerCustomer.count() // Reads its own snapshot.

// If you need the data again later, build a new DataFrame from the source tables.
oct15Orders = spark.table("main.sales.orders").filter(col("order_date") === "2026-10-15")

df.storageLevel

df의 저장 수준을 가져옵니다.

  • 반환: df가 캐시된 동안 StorageLevel.DISK_ONLY을 반환합니다. StorageLevel.NONE을 캐시하기 전과 df.unpersist()을 호출한 후.

Python

from pyspark import StorageLevel

oct_15_orders.cache()
oct_15_orders.storageLevel == StorageLevel.DISK_ONLY  # True

Scala

import org.apache.spark.storage.StorageLevel

oct15Orders.cache()
oct15Orders.storageLevel == StorageLevel.DISK_ONLY // true

메서드 시그니처에 대해서는 cache, persist, unpersist, storageLevel에 관한 PySpark 참조를 참조하세요.

수명 주기 및 시간 제한

캐시된 데이터는 데이터를 캐시한 Azure Databricks 세션에서만 존재합니다. 세션이 끝나면 접근 불가능해지며, 최대 7일 동안 지속됩니다. 7일 제한이 지나면 캐시된 DataFrame을 사용하면 소스 테이블을 다시 읽는 대신 오류가 발생합니다. 다시 캐시를 하려면 새 세션을 시작하세요.

스토리지 및 정리

Azure Databricks는 캐시된 데이터의 저장소를 자동으로 관리합니다. 캐시된 데이터는 클라우드 저장소에 저장되며, 구체적인 저장 위치는 작업 공간 유형에 따라 다릅니다:

  • 클래식 작업 공간: 캐시된 데이터는 작업 공간 생성 시 설정된 작업 공간 저장소 계정에 저장됩니다. 스토리지 계정 수준에서 암호화를 설정할 수 있습니다.

캐시된 데이터가 접근 불가능해지면 Azure Databricks가 백그라운드에서 자동으로 저장소를 회수합니다. 여기에는 연결이 끊기거나 크래시로 세션이 종료되는 경우도 포함됩니다. df.unpersist()를 호출하면 Azure Databricks가 더 일찍 저장소를 회수할 수 있습니다.

격리 및 권한

서버리스 컴퓨트에서 코드를 실행할 수 있는 모든 사용자가 DataFrame을 캐시할 수 있습니다. 추가 권한은 필요 없어요.

캐시된 데이터는 세션 수준의 격리를 제공합니다. 데이터프레임을 캐시한 세션만이 그 캐시된 데이터를 읽을 수 있습니다. 다른 사용자들, 같은 노트를 사용하는 다른 사용자들도 접근할 수 없습니다.

캐시된 데이터는 캐시할 때 읽을 수 있었던 내용을 담고 있습니다. 이후 소스 테이블에 적용된 권한 변경, 행 필터, 열 마스크는 캐시된 데이터에는 적용되지 않습니다.

모범 사례

  • 안정적인 스냅샷이 필요할 때 캐시: 동일한 계산된 데이터에 대해 여러 변환, 집계, 드릴다운을 실행하면, 스냅샷은 소스 테이블이 바뀌어도 변하지 않는 일관된 기준선을 제공합니다.
  • 비용이 많이 드는 계산은 캐시하고, 단순한 테이블 읽기는 캐시하지 마세요: df.cache() 쿼리 결과를 구체화하고, 이후 읽기 작업은 해당 스냅샷을 스캔합니다. 일반 테이블 리드를 캐시하면, spark.table("main.sales.orders").cache()와 같은 경우에도 이후 읽기는 여전히 테이블의 전체 복사본을 스캔하므로 캐싱으로 절약되는 것은 거의 없습니다. 캐싱은 조인, 집계, 복잡한 필터 같은 비용이 많이 드는 작업 후에 효과를 발휘합니다.
  • 한 번만 읽는 데이터를 캐시하지 마세요: df.cache()는 결과를 바로 계산하고 저장하는데, 이 과정에는 시간과 저장 공간이 필요합니다. 결과를 한 번만 읽으면 초기 비용이 그만한 가치가 없습니다.
  • 캐시된 DataFrame에서 파생하여 캐시 사용: 스냅샷을 읽으려면 캐시된 DataFrame 변수로부터 후속 연산을 빌드하세요. 예를 들어 df.select(...) 또는 df.filter(...). 소스 테이블에서 동일한 계산을 기반으로 만든 독립적인 DataFrame은 캐시를 사용하지 않습니다.
  • 크로스 세션이나 크로스 사용자 데이터 공유에 캐시를 사용하지 마세요: 캐시된 데이터는 현재 세션에만 한정됩니다. 다른 세션, 노트북, 작업 실행, 그리고 사용자들은 그것을 읽을 수 없습니다. 세션 간 또는 다른 사용자와 계산된 결과를 공유하려면 영구 테이블에 저장하세요.
  • 캐시된 데이터가 포함된 세션을 7일 이내로 유지하세요: 라이프사이클 및 시간 제한을 참조하세요. 워크플로우에 데이터를 더 오래 보관해야 한다면, 테이블에 저장하세요.
  • 캐시 데이터가 더 이상 필요 없을 때 df.unpersist()를 호출하세요: 이렇게 하면 Azure Databricks가 더 일찍 저장소를 되찾을 수 있습니다. 통화 후에는 DataFrame과 그로부터 파생된 DataFrame들을 사용할 수 없습니다.
  • 데이터를 새로고침하는 데 df.unpersist()를 사용하지 마세요: df.unpersist() 스냅샷의 수명이 종료됩니다. 소스 테이블의 변경 사항을 반영하려면 소스 테이블에서 새로운 DataFrame을 구축하세요.

제한 사항

서버리스 컴퓨트에서의 DataFrame 캐싱에는 다음과 같은 제한이 있습니다:

  • 열 이름 및 유형: 다음 DataFrame들은 캐시할 수 없습니다. 캐시하기 전에 열의 이름을 바꾸거나 캐스팅하세요. 예를 들어 .alias("total_amount")를 사용하여.
    • 중복된 열 이름을 가진 DataFrame들.
    • 공백이나 ,;{}()=의 문자 중 하나를 포함하는 열 이름을 가진 DataFrame(예: 집계에서 나온 sum(amount)).
    • 구간 열이 있는 데이터프레임.
    • NULL 타입 값을 포함하는 배열, 맵 또는 구조체를 가진 DataFrame.
    • 열이 없거나 NULL 타입 열만 있는 DataFrame.
  • 열 참조: DataFrame을 캐시한 후에는 그것이 생성된 DataFrame들을 통해 그 열을 참조할 수 없습니다. 예를 들어, oct_15_orders.cache() 및 oct_15_orders = orders.join(customers, "customer_id") 후, oct_15_orders.select(orders.amount)가 CANNOT_RESOLVE_DATAFRAME_COLUMN를 올립니다. 대신 oct_15_orders.amount 또는 "amount"를 사용하십시오.
  • SQL 및 카탈로그 캐시 API: CACHE TABLE와 같은 SQL 캐시 문과 spark.catalog의 테이블 캐시 메서드는 서버리스 컴퓨트에서 지원되지 않습니다. 캐싱 제한 사항을 참조하세요. SQL에서 쿼리 결과를 재사용하려면 세션 로컬 임시 테이블을 사용하세요.
  • 정보 스키마 쿼리: 메타스토어 캐시에 많은 세션이 데이터프레임을 캐시할 때, information_schema.volumes 및 information_schema.schemata에 대한 쿼리가 느리거나 시간 초과될 수 있습니다. 카탈로그별로 volumes를 필터링한다고 해서 쿼리가 더 빨라지지는 않습니다. information_schema.tables에 대한 쿼리는 영향을 받지 않습니다.
  • Azure Government regions: Azure Government regions에서는 Serverless compute에서 DataFrame 캐싱을 지원하지 않습니다.

클래식 컴퓨트에서 마이그레이션하기

대부분의 워크로드에서 서버리스 컴퓨트의 캐싱은 클래식 컴퓨트와 동일한 결과를 제공하며, 기존 코드는 변경 없이 실행됩니다. 다음 표에서는 차이점을 나열합니다.

Behavior 서버리스 컴퓨팅 클래식 컴퓨팅
캐시 일치 캐시된 DataFrame과 그로부터 파생된 DataFrame만이 자동으로 캐시를 사용합니다. 동일한 계획 형태의 쿼리는 자동으로 캐시를 사용합니다.
데이터 신선도 df.cache()를 호출한 시점의 일관된 스냅샷입니다. 경우에 따라 다릅니다: 캐시된 데이터는 특정 경우에 새로고침되고, 다른 경우에는 스냅샷을 제공합니다.
공유 범위 이 세션에만 비공개입니다. 클러스터 내 세션 간에 공유됩니다.
구체화 즉시: df.cache()를 호출하면 바로 계산됩니다. 지연: 데이터프레임에 대해 처음 작업할 때 계산됩니다.
df.unpersist() 데이터프레임을 삭제하고 스냅샷을 확정적으로 해제합니다. 메모리 내 캐시를 해제합니다. 이후 DataFrame 사용에서는 제품과 데이터 소스에 따라 최신 또는 오래된 데이터를 얻을 수 있습니다.

다음은 처음 두 가지 차이점에 대해 더 자세히 설명합니다:

  • 캐시 매칭:
    • 변경 영향 받는 워크로드: 대부분의 워크로드는 영향을 받지 않습니다. 예를 들어 df.select(...) 또는 df.filter(...)와 함께 캐시된 DataFrame 변수를 계속 사용하면 캐시는 이전과 동일하게 작동합니다. 이 변경은 독립적으로 생성된 DataFrame 또는 SQL 쿼리가 동일한 계획 형태를 가져 캐시를 재사용하는 워크로드에만 영향을 미칩니다. 서버리스 컴퓨트에서는 쿼리가 소스 테이블을 읽습니다.
    • 워크로드가 영향을 받는 경우: 같은 쿼리를 다시 만들지 말고, 캐시된 DataFrame 변수를 필요한 곳에 사용하세요.
  • 데이터 최신성:
    • 변경 영향 받는 워크로드: 대부분의 워크로드는 영향을 받지 않는데, 소스 테이블에 쓰기 후 캐시된 데이터를 다시 읽는 것은 드물기 때문입니다. 이 변경은 소스 테이블에 쓴 다음 캐시된 DataFrame이 새 데이터를 표시할 것으로 기대하는 워크로드에만 영향을 줍니다.
    • 작업 부하가 영향을 받는다면: 소스 테이블에 쓰기 후에는 캐시된 DataFrame을 재사용하지 않고 새 데이터프레임을 생성하세요.

추가 리소스