Apache Arrow 格式如何加速查詢結果傳輸


已發布 2025 年 1 月 10 日
作者: Ian Cook, David Li, Matt Topol

這是系列文章中的第一篇,旨在闡明 Arrow 作為資料庫和查詢引擎的資料交換格式之用途。

「為什麼要這麼久?」

這是資料從業人員在等待查詢結果時經常思考的問題。這個問題有很多可能的答案。也許您的資料來源分割不良。也許您的 SaaS 資料倉儲規模不足。也許查詢最佳化工具未能將您的 SQL 陳述式轉換為有效的執行計畫。

但令人驚訝的是,答案通常是您正在使用效率低下的協定將查詢結果傳輸到用戶端。在 2017 年的論文中,Mark Raasveldt 和 Hannes Mühleisen 觀察到,查詢結果傳輸時間通常佔據查詢執行時間的大部分,尤其是對於較大的結果。然而,瓶頸並不如您預期的那樣。

將查詢結果從來源傳輸到目的地涉及三個步驟

  1. 在來源端,將結果從其原始格式序列化為傳輸格式。
  2. 以傳輸格式透過網路傳輸資料。1
  3. 在目的地端,將傳輸格式反序列化為目標格式。

在網路速度較慢的時代,傳輸步驟通常是瓶頸,因此幾乎沒有動力去加速序列化和反序列化步驟。相反地,重點是縮小傳輸的資料大小,通常使用壓縮,以減少傳輸時間。正是在這個時代,設計了最廣泛使用的資料庫連線 API(ODBC 和 JDBC)和資料庫用戶端協定(例如 MySQL 用戶端/伺服器協定和 PostgreSQL 前端/後端協定)。但隨著網路變得更快且傳輸時間縮短,瓶頸已轉移到序列化和反序列化步驟。2 對於產生較大結果大小的查詢而言尤其如此,這也是許多資料工程和資料分析管線的特徵。

然而,今天許多查詢結果仍然透過舊版 API 和協定傳輸,這些 API 和協定透過將資料強制轉換為效率低下的傳輸格式,增加了大量的序列化和反序列化(「ser/de」)額外負擔。在 2021 年的論文中,Tianyu Li 等人提出了一個使用 ODBC 和 PostgreSQL 協定的範例,其中 99.996% 的總查詢時間花費在 ser/de 上。這可以說是一個極端案例,但我們在許多實際案例中觀察到 90% 或更高。如今,對於資料工程和資料分析查詢,強烈建議選擇一種可以加速 ser/de 的傳輸格式。

認識 Arrow。

Apache Arrow 開源專案定義了一種 資料格式,旨在加速—並且在許多情況下消除—查詢結果傳輸中的 ser/de。自 2016 年創建以來,Arrow 格式和圍繞它建立的多語言工具箱已獲得廣泛使用,但 Arrow 如何能夠大幅減少 ser/de 額外負擔的技術細節仍然鮮為人知。為了幫助解決這個問題,我們概述了 Arrow 格式的五個關鍵屬性,使其成為可能。

1. Arrow 格式是欄式。

欄式(column-oriented)資料格式將每欄的值保存在連續的記憶體區塊中。這與列式資料格式形成對比,後者將每列的值保存在連續的記憶體區塊中。

Figure 1: An illustration of row-oriented and column-oriented physical memory layouts of a table containing three rows and five columns.
圖 1:包含三列五欄的表格的列式和欄式實體記憶體佈局的說明。

高效能分析資料庫、資料倉儲、查詢引擎和儲存系統已經趨向於欄式架構,因為它可以加速最常見類型的分析查詢。現代欄式查詢系統的範例包括 Amazon Redshift、Apache DataFusion、ClickHouse、Databricks Photon Engine、DuckDB、Google BigQuery、Microsoft Azure Synapse Analytics、OpenText Analytics Database (Vertica)、Snowflake 和 Voltron Data Theseus。

同樣地,分析查詢結果的許多目的地(例如商業智慧工具、資料應用程式平台、資料框程式庫和機器學習平台)都使用欄式架構。欄式商業智慧工具的範例包括 Amazon QuickSight、Domo、GoodData、Power BI、Qlik Sense、Spotfire 和 Tableau。欄式資料框程式庫的範例包括 cuDF、pandas 和 Polars。

因此,查詢結果的來源格式和目標格式都越來越常見為欄式格式。在欄式來源和欄式目標之間傳輸資料最有效的方式是使用欄式傳輸格式。這消除了在序列化步驟期間,在來源端將資料從欄轉置為列的耗時需求,以及在反序列化步驟期間,在目的地端將資料從列轉置為欄的另一個耗時需求。

Arrow 是一種欄式資料格式。Arrow 格式中資料的欄式佈局與許多廣泛使用的欄式來源系統和目的地系統中資料的佈局相似—並且在許多情況下是相同的。

2. Arrow 格式是自我描述且類型安全的。

在自我描述資料格式中,結構描述(欄的名稱和類型)以及描述資料結構的其他中繼資料都包含在資料中。自我描述格式為接收系統提供了安全有效地處理資料所需的所有資訊。相比之下,當格式不是自我描述時,接收系統必須掃描資料以推斷其結構描述和結構(一個緩慢且容易出錯的過程),或單獨取得結構描述。

某些自我描述資料格式的一個重要屬性是能夠強制執行類型安全。當格式強制執行類型安全時,它可以保證資料值符合其指定的類型,從而允許接收系統排除在處理資料時發生類型錯誤的可能性。相比之下,當格式不強制執行類型安全時,接收系統必須檢查資料中每個單獨值的有效性(一個計算成本很高的過程)或在處理資料時處理類型錯誤。

當從非自我描述、類型不安全的格式(例如 CSV)讀取資料時,所有這些掃描、推斷和檢查都會導致大量的反序列化額外負擔。更糟糕的是,此類格式可能會導致歧義、偵錯問題、維護挑戰和安全性漏洞。

Arrow 格式是自我描述且強制執行類型安全。此外,Arrow 的類型系統與許多廣泛使用的資料來源和目的地的類型系統相似—並且在許多情況下是相同的或超集。這包括大多數欄式資料系統和許多列式系統,例如 Apache Spark 和各種關聯式資料庫。當使用 Arrow 格式時,這些系統可以快速且安全地在它們的原生類型和對應的 Arrow 類型之間轉換資料值。

3. Arrow 格式支援零複製。

零複製操作是指將資料從一種媒介傳輸到另一種媒介,而無需建立任何中間副本的操作。當資料格式支援零複製操作時,這表示其在記憶體中的結構與其在磁碟或網路上的結構相同。因此,例如,資料可以直接從網路讀取到記憶體中的可用結構中,而無需執行任何中間複製或轉換。

Arrow 格式支援零複製操作。為了保存資料值集,Arrow 定義了一種稱為 記錄批次 的欄式表格資料結構。Arrow 記錄批次可以保存在記憶體中、透過網路傳送或儲存在磁碟上。二進位結構保持不變,無論記錄批次在哪種媒介上以及哪個系統產生它。為了保存結構描述和其他中繼資料,Arrow 內部使用 FlatBuffers,這是一種由 Google 建立的格式,它也具有相同的二進位結構,無論它在哪種媒介上。

由於這些設計選擇,Arrow 不僅可以用作傳輸格式,還可以用作記憶體內格式和磁碟格式。這與基於文字的格式(例如 JSON 和 CSV)和序列化二進位格式(例如 Protocol Buffers 和 Thrift)形成對比,後者使用專用的結構語法來編碼資料值。為了將資料從這些格式載入到可用的記憶體內結構中,必須對資料進行剖析和解碼。這也與二進位格式(例如 Parquet 和 ORC)形成對比,後者使用編碼和壓縮來減少磁碟上資料的大小。為了將資料從這些格式載入到可用的記憶體內結構中,必須對其進行解壓縮和解碼。3

這表示在來源系統中,如果資料以 Arrow 格式存在於記憶體中或磁碟上,則可以以 Arrow 格式透過網路傳輸該資料,而無需任何序列化。在目的地系統中,可以將 Arrow 格式的資料從網路讀取到記憶體中或磁碟上的 Arrow 檔案中,而無需任何反序列化。

Arrow 格式被設計為作為分析操作的記憶體內格式非常有效。因此,許多欄式資料系統都使用 Arrow 作為其記憶體內格式而建構。這些包括 Apache DataFusion、cuDF、Dremio、InfluxDB、Polars、Velox 和 Voltron Data Theseus。當這些系統之一是傳輸的來源或目的地時,可以完全消除 ser/de 額外負擔。對於大多數其他欄式資料系統,它們使用的專有記憶體內格式與 Arrow 非常相似。對於這些系統,序列化為 Arrow 格式和從 Arrow 格式反序列化是快速且有效的。

4. Arrow 格式支援串流。

可串流資料格式是一種可以循序處理的格式,一次處理一個區塊,而無需等待完整資料集。當資料以可串流格式傳輸時,接收系統可以在第一個區塊到達時立即開始處理它。這可以透過多種方式加速資料傳輸:傳輸時間可以與處理時間重疊;接收系統可以更有效率地使用記憶體;並且可以並行傳輸多個串流,從而加速傳輸、反序列化和處理。

CSV 是可串流資料格式的一個範例,因為欄名稱(如果包含)在檔案頂部的標頭中,並且可以循序處理檔案中的行。Parquet 和 ORC 是不支援串流的資料格式範例,因為處理資料所需的結構描述和其他中繼資料位於檔案底部的頁尾中,因此必須下載整個檔案(或搜尋到檔案末尾並單獨下載頁尾),然後才能開始任何處理。4

Arrow 是一種可串流資料格式。資料集可以在 Arrow 中表示為一系列具有相同結構描述的記錄批次。Arrow 定義了一種 串流格式,該格式由結構描述和一個或多個記錄批次組成。接收 Arrow 串流的系統可以在記錄批次到達時循序處理它們。

Figure 2: An illustration of an Arrow stream transmitting data from a table with three columns. The first record batch contains the values for the first three rows, the second record batch contains the values for the next three rows, and so on. Actual Arrow record batches might contain thousands to millions of rows.
圖 2:說明從具有三欄的表格傳輸資料的 Arrow 串流。第一個記錄批次包含前三列的值,第二個記錄批次包含接下來三列的值,依此類推。實際的 Arrow 記錄批次可能包含數千到數百萬列。

5. Arrow 格式是通用的。

Arrow 已成為記憶體中處理表格資料的事實標準格式。Arrow 格式是一種與語言無關的開放標準。程式庫可用於處理 C、C++、C#、Go、Java、JavaScript、Julia、MATLAB、Python、R、Ruby、Rust 和 Swift 等語言的 Arrow 資料。以幾乎任何主流語言開發的應用程式都可以新增對以 Arrow 格式發送或接收資料的支援。資料不需要像某些資料庫連線 API(包括 JDBC)那樣,透過特定的語言執行階段。

Arrow 的通用性使其能夠解決加速實際資料系統中的一個基本問題:效能改進本質上受到系統瓶頸的限制。這個問題被稱為 阿姆達爾定律。在實際的資料管線中,查詢結果通常會流經多個階段,在每個階段都會產生 ser/de 額外負擔。例如,如果您的資料管線有五個階段,並且您消除了其中四個階段的 ser/de 額外負擔,則您的系統可能與以前一樣慢,因為剩餘一個階段的 ser/de 將會成為整個管線的瓶頸。

Arrow 在幾乎任何技術堆疊中有效運作的能力有助於解決這個問題。您的資料是否從基於 Scala 的分散式後端(具有 NVIDIA GPU 加速的工作程序)流向基於 Jetty 的 HTTP 伺服器,然後流向基於 Rails 的特徵工程應用程式,用戶透過基於 Node.js 的機器學習架構(具有基於 Pyodide 的瀏覽器前端)與之互動?沒問題;Arrow 程式庫可用於消除所有這些組件之間的 ser/de 額外負擔。

結論

隨著越來越多的商業和開源工具新增對 Arrow 的支援,具有低或無 ser/de 額外負擔的快速查詢結果傳輸已變得越來越普遍。如今,包括 Databricks、Dremio、Google BigQuery、InfluxDB、Snowflake 和 Voltron Data Theseus 在內的商業資料平台和查詢引擎,以及包括 Apache DataFusion、Apache Doris、Apache Spark、ClickHouse 和 DuckDB 在內的開源資料庫和查詢引擎,都可以使用 Arrow 格式傳輸查詢結果。速度提升顯著

在接收端,資料從業人員可以透過使用基於 Arrow 的工具和 Arrow 程式庫、介面和協定來最大化速度提升。在 2025 年,隨著越來越多的專案和供應商實作對 ADBC 標準的支援,我們預計會看到可以接收 Arrow 格式查詢結果的工具數量加速成長。

敬請關注本系列即將發布的文章,這些文章將比較 Arrow 格式與其他資料格式,並描述用戶端可用於以 Arrow 格式提取結果的協定和 API。


  1. 傳輸格式也可能稱為線路格式或序列化格式。 

  2. 從 1990 年代至今,網路效能的提升速度超過了 CPU 效能的提升速度。例如,在 1990 年代後期,主流桌上型電腦 CPU 的效能大約為 1 GFLOPS,而典型的 WAN 連線速度為 56 Kb/s。如今,主流桌上型電腦 CPU 的效能大約為 100 GFLOPS,而 WAN 連線速度約為 1 Gb/s 已很常見。因此,雖然 CPU 效能提高了約 100 倍,但網路速度提高了約 10,000 倍。 

  3. 這並不表示 Arrow 在其他應用程式(例如封存儲存)中比 Parquet 或 ORC 更快。本系列即將發布的文章將更詳細地比較 Arrow 格式與這些格式和其他格式,並描述它們通常如何互補。 

  4. 這並不表示 CSV 的傳輸結果會比 Parquet 或 ORC 更快。在比較 CSV 與 Parquet 或 ORC 的傳輸效能時,此處描述的其他屬性通常會超過這一個屬性。