Buforuj ramki danych w bezserwerowym środowisku obliczeniowym

Important

Ta funkcja jest dostępna w publicznej wersji testowej. Administratorzy przestrzeni roboczej mogą kontrolować dostęp do buforowania DataFrame na bezserwerowych zasobach obliczeniowych na stronie Wersje zapoznawcze. Zobacz Zarządzanie wersjami zapoznawczami usługi Azure Databricks.

W bezserwerowym środowisku obliczeniowym możesz buforować ramkę DataFrame za pomocą df.cache() lub df.persist(), aby ponownie wykorzystać kosztowny wynik w swojej sesji. Azure Databricks oblicza DataFrame podczas wywołania cache() i materializuje wynik jako migawkę. DataFrame i obiekty DataFrame, które z niego budujesz, odczytują migawkę przez resztę sesji zamiast ją ponownie obliczać.

Requirements

  • Wersja zapoznawcza buforowania DataFrame w bezserwerowych zasobach obliczeniowych jest włączona dla Twojej przestrzeni roboczej (domyślnie włączona). Administratorzy obszaru roboczego mogą wyłączyć tę funkcję na stronie Podglądów. Podgląd nie jest dostępny w obszarach roboczych z włączonym profilem zgodności zabezpieczeń. Zobacz Zarządzanie podglądami na poziomie obszaru roboczego.
  • Notebooki i zadania w bezserwerowym przetwarzaniu muszą korzystać z wersji środowiska 5 lub nowszej.
  • Databricks Connect dla Python musi mieć wersję 18.1.8 lub nowszą, a Databricks Connect dla Scali musi być na 18.1.5 lub nowszej.

Starsi klienci otrzymują błąd CACHE_REQUIRES_NEWER_CLIENT_ON_SERVERLESS_GC.

Kiedy używać buforowania DataFrame w obliczeniach serverless

Buforuj obiekt DataFrame w bezserwerowych zasobach obliczeniowych, gdy potrzebujesz:

  • Wykorzystaj wynik kosztownego połączenia, agregacji lub filtra wielokrotnie w tej samej sesji.
  • Oblicz mikropartię strumieniową raz w funkcji foreachBatch, a następnie zapisz ją do kilku ujść.
  • Wytrenuj lub dostrój kilka modeli uczenia maszynowego na tych samych przygotowanych danych cech.
  • Utrzymuj stabilny snapshot podczas wykonywania kilku etapów analizy, nawet jeśli tabele źródłowe się zmieniają.

Zobacz Najlepsze rozwiązania.

Buforuj interfejsy API DataFrame w środowisku bezserwerowym

Poniższy przykład buforuje zamówienia z jednego dnia połączone z danymi klienta, oblicza całkowite przychody z danego dnia, dodaje przychody według regionów do tabeli raportowej, a następnie zwalnia bufor pamięci podręcznej. Oba wyniki odczytują migawkę z pamięci podręcznej zamiast ponownie uruchamiać operację JOIN.

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

W poniższych opisach API symbol df oznacza dowolny DataFrame w twojej sesji, taki jak oct_15_orders w poprzednim przykładzie.

df.cache()

Oblicza df to od razu i zapisuje wynik jako snapshot na resztę sesji.

  • Zwraca: Ten sam buforowany DataFrame, df, teraz oparty na migawce zamiast na obliczeniach z jego tabel źródłowych. Zobacz następujące działanie.
  • Zachowanie:
    • Natychmiast uruchamia zapytanie DataFrame i materializuje wynik jako migawkę, zanim wywołanie zwróci wynik.
    • Po wywołaniu df oraz zwrócony DataFrame (df sam w sobie) opierają się na migawce. Nie są już połączone z obliczeniem wykonywanym na tabelach źródłowych, więc zamiast ponownie obliczać zapytanie, odczytują migawkę.
    • DataFrame'y pochodzące z df, bezpośrednio lub za pomocą innych pochodnych DataFrames, automatycznie odczytują migawkę zamiast ponownie uruchamiać oryginalne zapytanie. Zobacz Odczytaj zrzut.
    • Migawka jest lokalna dla Twojej sesji. Tylko sesja, która zachowała df w pamięci podręcznej, może go odczytać. Zobacz Izolację i przywileje.
    • Migawka trwa do końca sesji, maksymalnie przez siedem dni. Azure Databricks automatycznie odzyskuje swoją pamięć masową w tle. Wywołanie df.unpersist() pozwala mu wcześniej zwolnić pamięć. Zobacz cykl życia i limity czasowe oraz Przechowywanie i czyszczenie.
    • Jeśli obliczanie df zawiedzie, df.cache() zgłasza błąd i df nie jest buforowane.
    • Wywołanie df.cache() na dataFrame, który jest już w pamięci podręcznej, nic nie daje. df nie jest ponownie obliczane.

Migawka zostaje naprawiona po wywołaniu df.cache(). Dane zapisane później do tabel, z których df odczytuje, oraz zmiany schematu w tych tabelach, takie jak dodane lub przemianowane kolumny, nie mają na to wpływu. Aby przeliczyć z najnowszych danych, zbuduj DataFrame ponownie z tabel źródłowych, a jeśli trzeba, zbuforuj nowy DataFrame.

Przeczytaj zrzut

W obliczeniach bezserwerowych dopasowanie cache opiera się na pochodzeniu obiektu DataFrame. Po df.cache(), tylko następujące DataFrame'y automatycznie odczytują migawkę zamiast ponownie obliczać zapytanie na tabelach źródłowych:

  • DataFrame, który zapisano w pamięci podręcznej, df.
  • Ramki danych pochodzące z df, na przykład przy użyciu filter(), select(), groupBy() lub join().
  • DataFrames były dalej łączone w łańcuch od tych pochodnych DataFrame'ów.

Nowy DataFrame, który powtarza to samo zapytanie, odczytuje tabele źródłowe, mimo że jego logika jest identyczna, ponieważ nie jest wyprowadzony z df.

To sprawia, że dopasowanie do pamięci podręcznej jest jawne i pod Twoją kontrolą: DataFrame odczytuje dane z pamięci podręcznej tylko wtedy, gdy wyprowadzasz je z tego DataFrame'a. Co drugi DataFrame odczytuje tabele źródłowe.

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

Ponieważ df czyta snapshot zamiast DataFrame'ów, z których został zbudowany, nie możesz odwoływać się do kolumn df za pośrednictwem tych DataFrame'ów. Zobacz Ograniczenia.

df.persist(storageLevel)

Alias df.cache(), który przyjmuje poziom przechowywania.

  • Parametry:
    • storageLevel (opcjonalnie): StorageLevel, na przykład StorageLevel.DISK_ONLY. Obliczenia bezserwerowe akceptują dowolny poziom pamięci i ignorują go. Migawka jest przechowywana w ten sam sposób dla każdego poziomu pamięci i df.storageLevel zwraca StorageLevel.DISK_ONLY.

Wartość zwrotu i zachowanie są takie same jak df.cache().

df.unpersist(blocking)

Uwalnia zbuforowaną migawkę df i niszczy DataFrame.

  • Parametry:
    • blocking (opcjonalnie): Ignorowane. df.unpersist() zwraca od razu dla każdej wartości.
  • Zwraca: Ten sam DataFrame, teraz zniszczony.
  • Zachowanie:
    • Oznacza migawkę jako gotową do odzyskania. Azure Databricks następnie usuwa dane z pamięci podręcznej i odzyskuje miejsce na dysku w tle.
    • Niszczy df: df oraz wszelkie DataFrame'y wcześniej z niego wyprowadzone nie są już użyteczne, a każde kolejne zapytanie na nich powoduje CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE. Jeśli buforujesz DataFrame pochodzący z df, zanim wywołasz df.unpersist(), ten DataFrame pozostaje użyteczny, ponieważ ma własną migawkę.
    • Aby kontynuować pracę z tym samym źródłem danych, zbuduj nowy DataFrame z oryginalnego źródła danych, zamiast ponownie używać niebuforowanego DataFrame.
    • Wywołanie df.unpersist() na obiekcie DataFrame, który nigdy nie był buforowany lub został już odłączony od pamięci podręcznej, nie daje żadnego efektu.

Wywołaj df.unpersist(), gdy nie potrzebujesz już danych w pamięci podręcznej, na przykład gdy żaden kolejny krok w twoim notatniku lub zadaniu ich nie odczyta. Pozwala to Azure Databricks wcześniej odzyskać zasoby magazynu. Nie używaj df.unpersist() do odświeżania danych. Aby pracować z najnowszymi danymi, zbuduj nowy DataFrame z tabel źródłowych.

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

Pobiera poziom pamięci df.

  • Zwrot:StorageLevel.DISK_ONLY podczas gdy df jest zapisane w pamięci podręcznej. StorageLevel.NONE przed zapisaniem go do pamięci podręcznej i po wywołaniu 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

Aby poznać sygnatury metod, zobacz referencję PySpark dla cache, persist, unpersist oraz storageLevel.

Limity czasu i cyklu życia

Dane buforowane istnieją tylko w sesji Azure Databricks, gdzie je buforujesz. Po zakończeniu sesji staje się niedostępna i jest dostępna maksymalnie przez siedem dni. Po upływie siedmiu dni użycie buforowanego DataFrame zwraca błąd zamiast ponownego odczytu tabel źródłowych. Aby ponownie zapisać w pamięci podręcznej, rozpocznij nową sesję.

Magazynowanie i czyszczenie

Azure Databricks automatycznie zarządza przechowywaniem danych w pamięci podręcznej. Dane w pamięci podręcznej są przechowywane w chmurze, a dokładne miejsce przechowywania zależy od typu przestrzeni roboczej:

  • Klasyczne przestrzenie robocze: Dane buforowane są przechowywane na koncie magazynu obszaru roboczego, które jest skonfigurowane w momencie tworzenia obszaru roboczego. Możesz skonfigurować szyfrowanie na poziomie konta pamięci.

Gdy dane w pamięci podręcznej stają się niedostępne, Azure Databricks automatycznie odzyskuje miejsce magazynowe w tle. Dotyczy to także przypadków, gdy sesja kończy się z powodu rozłączenia lub awarii. Wywołanie df.unpersist() pozwala usłudze Azure Databricks wcześniej odzyskać przestrzeń dyskową.

Izolacja i uprawnienia

Każdy użytkownik, który potrafi uruchamiać kod na bezserwerowych zasobach obliczeniowych, może buforować ramkę danych. Nie potrzebujesz żadnych dodatkowych uprawnień.

Dane buforowane zapewniają izolację na poziomie sesji. Tylko sesja, która zbuforowała DataFrame, może odczytać jego dane w pamięci podręcznej. Inni użytkownicy nie mają do niego dostępu, w tym inni użytkownicy tego samego notesu.

Dane w pamięci podręcznej zawierają to, co mogłeś odczytać, gdy zapisano je w pamięci podręcznej. Zmiany uprawnień, filtry wierszy i maski kolumn stosowane później do tabel źródłowych nie mają zastosowania do danych buforowanych.

Najlepsze praktyki

  • Buforuj, gdy potrzebujesz stabilnego snapshota: Jeśli wykonasz kilka transformacji, agregacji lub drill-downów na tych samych obliczonych danych, snapshot daje im spójny punkt odniesienia, który nie zmienia się przy zmianie tabel źródłowych.
  • Buforuj kosztowne obliczenia, a nie zwykłe odczyty tabelowe: df.cache() materializuje wynik zapytania, a późniejsze odczyty skanują tę migawkę. Jeśli buforujesz zwykły odczyt tabeli, taki jak spark.table("main.sales.orders").cache(), późniejsze odczyty nadal skanują pełną kopię tabeli, więc buforowanie daje niewielkie oszczędności. Buforowanie przynosi efekty w przypadku kosztownych operacji, takich jak połączenia, agregacje i skomplikowane filtry.
  • Nie buforuj danych, które odczytujesz tylko raz: df.cache() oblicza i zapisuje wynik od razu, co zajmuje czas i wymaga miejsca do przechowywania. Jeśli przeczytasz wynik tylko raz, koszt początkowy nie jest tego wart.
  • Korzystaj z pamięci podręcznej, używając zbuforowanego obiektu DataFrame: Aby odczytać migawkę, buduj późniejsze operacje na podstawie zbuforowanej zmiennej DataFrame, na przykład df.select(...) lub df.filter(...). Niezależny DataFrame zbudowany na podstawie tych samych obliczeń na tabelach źródłowych nie korzysta z pamięci podręcznej.
  • Nie używaj pamięci podręcznej do udostępniania danych między sesjami lub użytkownikami: dane w pamięci podręcznej są prywatne dla Twojej bieżącej sesji. Inne sesje, notatniki, uruchomienia zadań i użytkownicy nie mogą go czytać. Aby udostępniać obliczone wyniki w różnych sesjach lub innym użytkownikom, zapisz je do trwałej tabeli.
  • Utrzymuj sesje z danymi w pamięci podręcznej poniżej siedmiu dni: zobacz Cykl życia i limity czasowe. Jeśli Twój workflow potrzebuje danych na dłużej, zapisz je w tabeli.
  • Wywołaj df.unpersist(), gdy nie będziesz już potrzebować danych z pamięci podręcznej: Pozwala to usłudze Azure Databricks szybciej odzyskać miejsce w magazynie. Po wywołaniu obiekt DataFrame i obiekty DataFrame z niego wyprowadzone nie mogą być używane.
  • Nie używaj df.unpersist() do odświeżania danych: df.unpersist() kończy życie snapshota. Aby wychwycić zmiany w tabelach źródłowych, zbuduj nowy DataFrame z tabel źródłowych.

Ograniczenia

Buforowanie DataFrame w bezserwerowych zasobach obliczeniowych ma następujące ograniczenia:

  • Nazwy i typy kolumn: Następujące DataFrame'y nie mogą być buforowane. Zmień nazwę kolumn lub rzutuj je przed zapisaniem w pamięci podręcznej, na przykład za pomocą .alias("total_amount").
    • DataFrames z duplikatami nazw kolumn.
    • DataFrames z nazwami kolumn zawierającymi spacje lub dowolne znaki ,;{}()=, na przykład sum(amount) pochodzącymi z agregacji.
    • DataFrames z kolumnami przedziałowymi.
    • DataFrames z tablicami, mapami lub strukturami zawierającymi wartości typu NULL.
    • DataFrames bez kolumn lub tylko z kolumnami typu NULL.
  • Odniesienia do kolumn: Po cacheowaniu DataFrame'a nie możesz odnosić się do jego kolumn przez DataFrame'y, z których został zbudowany. Na przykład po oct_15_orders = orders.join(customers, "customer_id") i oct_15_orders.cache(), oct_15_orders.select(orders.amount) podnosi CANNOT_RESOLVE_DATAFRAME_COLUMN. Użyj polecenia oct_15_orders.amount lub "amount" zamiast tego.
  • Interfejsy API pamięci podręcznej SQL i katalogu: Instrukcje pamięci podręcznej SQL, takie jak CACHE TABLE, oraz metody pamięci podręcznej tabel w spark.catalog nie są obsługiwane w serverless compute. Zobacz Ograniczenia buforowania. Aby ponownie wykorzystać wynik zapytania w SQL, użyj tymczasowej tabeli sesyjnej lokalnej.
  • Zapytania dotyczące schematu informacji: Gdy wiele sesji w metastore buforuje DataFrames, zapytania na information_schema.volumes i information_schema.schemata mogą być wolne lub przekraczać limit czasu. Filtrowanie volumes według katalogu nie przyspiesza zapytań. Zapytania dotyczące information_schema.tables nie są dotknięte.
  • Regiony Azure Government: Buforowanie DataFrame nie jest obsługiwane w serwerless compute w regionach Azure Government.

Migracja z klasycznych zasobów obliczeniowych

Dla większości obciążeń buforowanie na bezserwerowych zasobach obliczeniowych daje takie same wyniki jak na klasycznych zasobach obliczeniowych, a istniejący kod działa bez zmian. Poniższa tabela przedstawia różnice.

Behavior Bezserwerowe obliczenia Obliczenia klasyczne
Dopasowanie pamięci podręcznej Tylko zbuforowany DataFrame oraz pochodzące z niego DataFrames automatycznie korzystają z pamięci podręcznej. Każde zapytanie o tym samym kształcie planu automatycznie korzysta z pamięci podręcznej.
Aktualność danych Spójne zdjęcie wykonane w momencie, gdy wywołujesz df.cache(). Różni się: dane buforowane są odświeżane w niektórych przypadkach, a w innych wyświetlany jest stan z migawki.
Zakres współdzielenia Prywatne dla tej sesji. Udostępniane pomiędzy sesjami w klastrze.
Materializacja Eager: obliczane od razu, gdy wywołujesz df.cache(). Leniwe: obliczone przy pierwszej akcji na DataFrame.
df.unpersist() Niszczy DataFrame i deterministycznie uwalnia jego migawkę. Zwalnia pamięć podręczną. Przy późniejszym użyciu obiektu DataFrame można uzyskać najnowsze lub nieaktualne dane, w zależności od produktu i źródła danych.

Poniżej szczegółowo opisano dwie pierwsze różnice:

  • Dopasowywanie pamięci podręcznej:
    • Obciążenia robocze objęte zmianą: Większość obciążeń roboczych nie jest objęta zmianą. Jeśli będziesz dalej używać zmiennej DataFrame zapisanej w pamięci podręcznej, na przykład z df.select(...) lub df.filter(...), pamięć podręczna działa jak wcześniej. Zmiana dotyczy tylko obciążeń, w których niezależnie zbudowany DataFrame lub zapytanie SQL ponownie wykorzystuje pamięć podręczną, ponieważ ma ten sam kształt planu. W bezserwerowym środowisku obliczeniowym te zapytania odczytują tabele źródłowe.
    • Jeśli problem dotyczy Twojego obciążenia roboczego: Użyj buforowanej zmiennej DataFrame tam, gdzie potrzebujesz buforowanego wyniku, zamiast budować to samo zapytanie od nowa.
  • Świeżość danych:
    • Obciążenia robocze dotknięte zmianą: Większość obciążeń roboczych nie jest dotknięta, ponieważ ponowne odczytywanie danych z pamięci podręcznej po zapisie do tabel źródłowych jest rzadkie. Zmiana dotyczy tylko obciążeń, które zapisują do tabel źródłowych i oczekują, że buforowany DataFrame pokaże nowe dane.
    • Jeśli ma to wpływ na Twoje obciążenie robocze: Po zapisaniu do tabel źródłowych zbuduj z nich nowy DataFrame zamiast ponownie używać buforowanego DataFrame.

Dodatkowe zasoby