Important
這項功能目前處於 公開預覽版。 Workspace 管理員可從 Previews 頁面控制 無伺服器運算中的 DataFrame 快取 的存取權限。 請參閱 管理 Azure Databricks 預覽。
在無伺服器運算中,你可以使用 df.cache() 或 df.persist() 來快取資料框架,以便在工作階段中重複使用成本高昂的結果。 Azure Databricks 在你呼叫cache()時會計算 DataFrame,並將結果以快照形式實體化。 DataFrame 以及你從它建立的 DataFrame 之後會在該工作階段的其餘時間內讀取快照,而不是重新計算。
Requirements
- 已為您的工作區啟用 無伺服器運算上的 DataFrame 快取 預覽版(預設啟用)。 Workspace 管理員可以從 預覽 頁面關閉它。 在啟用 合規安全設定檔 的工作空間中,預覽無法使用。 請參閱管理工作區層級預覽。
- 無伺服器運算上的筆記本與工作必須使用 環境版本 5 或以上。
- Python 版 Databricks Connect 必須是 18.1.8 或以上,Scala 版 Databricks Connect 必須是 18.1.5 或以上。
較舊版的用戶端會出現 CACHE_REQUIRES_NEWER_CLIENT_ON_SERVERLESS_GC 錯誤。
何時在無伺服器運算中使用 DataFrame 快取
當您需要以下操作時,請在無伺服器運算上快取 DataFrame:
- 在同一個工作階段中,重複使用昂貴的連接、聚合或過濾結果多次。
- 在函
foreachBatch式中計算一次串流微批次,然後寫入多個接收端。 - 在同一個已準備好的特徵資料上訓練或調整多個機器學習模型。
- 即使來源資料表改變,執行多個分析步驟時也要保持穩定的快照。
請參閱最佳實務。
在無伺服器運算上快取 DataFrame API
以下範例會快取一天內與客戶資料合併的訂單,計算當天的總營收,將區域收入附加到報告表中,然後釋出快取。 兩個結果都是讀取快取快照,而非重新執行 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()
在以下 API 描述中,df 代表您工作階段中的任何 DataFrame,例如前述範例中的 oct_15_orders。
df.cache()
會立即計算 df ,並將結果快取為 快照 ,供你整個會話使用。
-
回傳:相同的快取 DataFrame,
df現在是以快照為基礎,而不是以其來源資料表上的運算為基礎。 請參考以下運作方式。 -
行為:
- 會立即執行 DataFrame 的查詢,並在呼叫返回前將結果具體化為快照。
- 呼叫結束後,
df和回傳的資料幀(df本身)都是基於快照。 它們不再連接到來源資料表的計算,因此會讀取快照,而非重新計算查詢。 - 直接或透過其他衍生資料框架,從
df衍生的資料框架會自動讀取快照,而不是再次執行原始查詢。 請參閱 閱讀快照說明。 - 快照是你的工作階段本地的。 只有已快取
df的工作階段才能讀取它。 參見 隔離與特權。 - 快照會持續到會話結束,最多七天。 Azure Databricks 會自動在背景中回收其儲存空間。 呼叫
df.unpersist()可以讓它更早回收儲存空間。 請參閱 生命週期與時間限制 ,以及 儲存與清理。 - 如果運算
df失敗,df.cache()會引發錯誤,且df不會被快取。 - 對已經快取的 DataFrame 呼叫
df.cache()不會執行任何操作。df不再計算。
當你呼叫 df.cache() 之後,快照會固定下來。 之後寫入到 df 讀取資料的資料表中的資料,以及對這些資料表的結構變更(如新增或重新命名欄位),都不會影響 df。 若要從最新資料重新計算,請從來源資料表重新建立 DataFrame,必要時快取新的 DataFrame。
檢視快照
在無伺服器運算中,快取比對是以 DataFrame 衍生為基礎。 之後 df.cache(),只有以下資料框架自動讀取快照,而非重新計算來源資料表的查詢:
- 你快取的 DataFrame
df。 - 由
df衍生出的資料框架,例如帶有select()、filter()、groupBy()、 或join()。 - 從這些衍生的 DataFrame 再進一步鏈結而來的 DataFrame。
一個新的 DataFrame,若重複執行相同查詢,會讀取來源資料表,儘管其邏輯相同,因為它並非源自 df。
這使得快取匹配變得明確且由你控制:DataFrame 只有在你從已快取的 DataFrame 衍生它時,才會讀取快取資料。 每兩個 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 讀取的是快照,而不是建立它所依據的資料框架,你無法透過這些資料框架來參考 df 的欄位。 請參閱限制。
df.persist(storageLevel)
接受儲存層級的 df.cache() 別名。
-
參數:
-
storageLevel(可選):一個StorageLevel,例如StorageLevel.DISK_ONLY。 無伺服器運算支援任何儲存層級並忽略它。 快照對每個儲存層級都以相同方式儲存,且df.storageLevel會回傳StorageLevel.DISK_ONLY。
-
回傳值與行為與 df.cache() 相同。
df.unpersist(blocking)
釋放 df 的已快取快照,並銷毀 DataFrame。
-
參數:
-
blocking(可選):忽略。df.unpersist()無論是哪個值,都會立即傳回。
-
-
回傳:同一個 DataFrame,
df現已銷毀。 -
行為:
- 標記快照為可回收。 Azure Databricks 接著會刪除快取的資料,並在背景回收儲存空間。
- 摧毀
df:df且先前從它衍生出的任何資料框架都不再可用,且對其的任何後續查詢都會引發CACHE_DATAFRAME_ACCESS_AFTER_UNCACHE。 如果你在呼叫df前快取了從df.unpersist()衍生出來的資料幀,該資料幀仍可使用,因為它有自己的快照。 - 若要繼續使用同一資料來源,請從原始資料來源建立新的 DataFrame,而非重複使用未快取的資料框架。
- 對從未快取過或已經解除快取的資料框呼叫
df.unpersist(),不會執行任何操作。
當你不再需要快取資料時,例如當你的筆記本或工作中的後續步驟不再讀取這些資料時,再呼叫 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
關於方法簽名,請參見 PySpark 關於 cache、 persist、 unpersist 和 storageLevel 的參考資料。
生命週期與時間限制
快取的資料只存在於你快取的 Azure Databricks 會話中。 當工作階段結束後,該系統將無法使用,且持續時間最多為七天。 超過七天限制後,使用快取的 DataFrame 會產生錯誤,而非重新讀取來源資料表。 若要再次快取,請開始新的工作階段。
儲存與清理
Azure Databricks 會自動管理快取資料的儲存空間。 快取的資料會儲存在雲端儲存,具體儲存位置依據你工作區類型而定:
- 無伺服器工作空間:快取資料儲存在 預設儲存體中。 如果你為託管服務設定了客戶管理的金鑰,快取資料會使用該金鑰。 請參閱適用於加密的客戶自控金鑰。
- 經典工作區:快取資料儲存在 工作區建立時設定的儲存帳戶中。 你可以在儲存帳號層級設定加密。
當快取資料無法存取時,Azure Databricks 會自動在背景收回儲存空間。 這包括因斷線或當機而結束的會話。 呼叫df.unpersist()可以讓 Azure Databricks 更早回收儲存空間。
孤立與特權
任何能在無伺服器運算上執行程式碼的使用者,都可以快取 DataFrame。 你不需要額外的特權。
快取資料提供會話層級的隔離。 只有快取 DataFrame 的會話能讀取其快取資料。 其他使用者無法存取,包括同一個筆記本的其他使用者。
快取的資料包含你快取時允許讀取的內容。 之後對來源資料表施加的權限變更、列篩選和欄位遮罩,則不會影響快取的資料。
最佳做法
- 需要穩定快照時的快取:如果你對同一計算資料執行多次轉換、聚合或深入分析,快照會給出一個一致的基準,且在來源資料表變動時不會改變。
-
快取高成本的運算,而非單純的讀表:
df.cache()將查詢結果具體化,之後讀取時掃描該快照。 如果你快取了純的表讀,例如spark.table("main.sales.orders").cache(),後續讀取仍會掃描完整的資料表副本,所以快取節省的資源有限。 快取在執行連接、彙總和複雜篩選等高成本作業後會見效。 -
不要快取只讀一次的資料:
df.cache()會立即計算並儲存結果,這需要時間和儲存空間。 如果你只讀一次結果,前期成本就不值得。 -
透過從已快取的 DataFrame 衍生來使用快取:要讀取快照,請從已快取的 DataFrame 變數建立後續操作,例如
df.select(...)或df.filter(...)。 一個獨立的 DataFrame 是根據來源資料表的相同計算所建構,不會使用快取。 - 不要用快取來跨會話或跨使用者資料分享:快取的資料對你的會話是私密的。 其他工作階段、筆記本、工作執行和使用者都無法讀取它。 若要在會話間或與其他使用者分享計算結果,請將其寫入永久表。
- 將快取資料的會話保持在七天以下:詳見 生命週期與時間限制。 如果你的工作流程需要將資料保留更久,就寫入資料表。
-
當你不再需要快取資料時,呼叫
df.unpersist():這讓 Azure Databricks 能更早回收儲存空間。 呼叫結束後,DataFrame 及其衍生的 DataFrame 無法再使用。 -
不要用
df.unpersist()來刷新資料:df.unpersist()會結束快照的生命週期。 要擷取來源資料表的變更,請從來源資料表建立新的 DataFrame。
Limitations
無伺服器運算中的 DataFrame 快取有以下限制:
-
欄位名稱與類型:以下 DataFrame 無法快取。 在快取前重新命名或轉型欄位,例如使用
.alias("total_amount")。- 欄位名稱重複的 DataFrame。
- 欄位名稱包含空格或任何字元
,;{}()=的資料框架,例如彙總結果中的sum(amount)。 - 帶有區間欄位的 DataFrames。
- 包含 NULL 型別值的陣列、對應或結構的資料框。
- DataFrame 沒有欄位,或只有 NULL 型別欄位。
-
欄位參考:快取 DataFrame 後,無法透過建立它時所依據的 DataFrame 來參考其欄位。 例如,在
oct_15_orders = orders.join(customers, "customer_id")和oct_15_orders.cache()之後,oct_15_orders.select(orders.amount)則會提高CANNOT_RESOLVE_DATAFRAME_COLUMN。 請改用oct_15_orders.amount或"amount"。 -
SQL 與目錄快取 API:SQL 快取陳述式(例如
CACHE TABLE)以及spark.catalog的資料表快取方法,不支援在無伺服器運算上使用。 詳見 快取限制。 若要在 SQL 中重用查詢結果,請使用 會話本地的暫存資料表。 -
資訊結構查詢:當元儲存庫中有多個工作階段快取 DataFrames 時,對
information_schema.volumes和information_schema.schemata的查詢可能會變慢或逾時。以目錄篩選volumes不會讓查詢更快。 查詢information_schema.tables不會受到影響。
- Azure Government regions:Azure Government 區域中無伺服器運算不支援 DataFrame 快取。
-
忽略參數:
df.persist(storageLevel)忽略儲存層級,df.unpersist(blocking)忽略blocking。 請參閱df.persist(storageLevel)與df.unpersist(blocking)。
從傳統運算遷移
對大多數工作負載來說,在無伺服器運算上快取的結果與傳統運算相同,且現有程式碼可不做變更地執行。 下表列出差異。
| Behavior | 無伺服器計算 | 傳統運算 |
|---|---|---|
| 快取比對 | 只有快取的 DataFrame 及其衍生的 DataFrame 會自動使用快取。 | 任何具有相同執行計畫形狀的查詢都會自動使用快取。 |
| 數據新鮮度 | 在你呼叫 df.cache() 的時候拍攝的一致性快照。 |
情況有所不同:快取資料在某些情況下會被刷新,在其他情況下則提供快取的快照結果。 |
| 共享範圍 | 只屬於你的工作階段。 | 在叢集內的多個會話間共享。 |
| 具象化 | 立即:當你呼叫 df.cache()時,立即計算。 |
懶惰:在 DataFrame 的第一個動作時計算。 |
df.unpersist() |
會銷毀 DataFrame 並在明確時機釋放其快照。 | 釋放記憶體中的快取。 後續使用資料框時,可能會取得最新或過時的資料,視產品與資料來源而定。 |
以下將更詳細說明前兩個差異:
-
快取比對:
-
受此變更影響的工作負載:大多數工作負載不會受到影響。 如果你持續使用快取的 DataFrame 變數,例如帶有
df.select(...)或df.filter(...),快取會如先前一樣運作。 此變更僅影響獨立建置的 DataFrame 或 SQL 查詢因具有相同的計畫形狀而重複使用快取的工作負載。 在無伺服器運算中,這些查詢會讀取來源資料表。 - 如果這會影響您的工作負載:請在需要快取結果時使用已快取的 DataFrame 變數,而不是重新建立相同的查詢。
-
受此變更影響的工作負載:大多數工作負載不會受到影響。 如果你持續使用快取的 DataFrame 變數,例如帶有
-
資料新近性:
- 受影響的工作負載:大多數工作負載不會受影響,因為寫入來源資料表後再次讀取快取資料並不常見。 這項變更只影響那些寫入來源資料表,然後預期快取的 DataFrame 會顯示新資料的工作負載。
- 如果你的工作負載受到影響:寫入來源資料表後,從它們建立新的 DataFrame,而不是重複使用已快取的 DataFrame。