大家好,我是 Cady,目前就讀資管系,也正在參與 Apache DataFusion 的開源貢獻。
發現 Day 1 忘記介紹自己,來補充一下,免得很像 AI 發文~
我對 Data Engineering 很感興趣,未來也想往資料工程的方向走,希望透過 30 天的鐵人賽,擴展自己對 Query Engine 的理解。
大家可能會有個疑問,就像標題的問句一樣:
為什麼選擇 Rust 與 Apache DataFusion?
今天就來回答這個問題!
昨天提到,一段 SQL 從送出到產生結果,中間會經過:
SQL Parsing
↓
Logical Plan
↓
Logical Optimization
↓
Physical Plan
↓
Physical Optimization
↓
Execution
↓
Result
這些工作不只是處理 SQL 字串。
當資料量增加時,Query Engine 還需要面對:
這也是 Rust 與 DataFusion 開始登場的地方。
Rust 是一門系統程式語言。它的特色不只是執行速度,也包括對記憶體安全、型別與並行程式的重視。
有些程式語言會使用 Garbage Collector(GC)自動回收不再使用的記憶體。
Rust 則透過 Ownership(所有權)、Borrowing(借用)與 Lifetime(生命週期)等規則管理記憶體。許多可能造成記憶體問題的操作,會在編譯階段被檢查。
這不代表 Rust 程式一定不會出錯,但它能提早阻止許多常見的記憶體使用錯誤。
對 Query Engine 而言,資料可能以一批一批的方式持續流經不同執行節點。如何管理這些資料的生命週期、避免不必要的複製,是很重要的問題。
Rust 會被編譯成原生程式碼,也提供低階的記憶體與資料結構控制能力。
Query Engine 經常需要進行:
這些工作可能需要處理大量資料,因此執行效率與資源使用會直接影響查詢速度。
當然,使用 Rust 不代表程式就會自動變快。資料結構、演算法、I/O 與 Query Plan 仍然很重要;Rust 提供的是一個兼顧效能與安全性的實作基礎。
現代 Query Engine 通常會利用多核心 CPU,同時處理不同 Partition 或 RecordBatch。
但是,多執行緒程式可能產生資料競爭、鎖使用錯誤或共享狀態管理問題。
Rust 的型別系統與所有權規則,能在編譯階段協助檢查部分並行存取問題。雖然剛開始可能會覺得編譯器很嚴格,但這些限制也能幫助開發者更明確地思考資料由誰持有、在哪裡被共享。
Apache DataFusion 是使用 Rust 實作、以 Apache Arrow 作為記憶體資料格式的開源 Query Engine。
它提供了許多建構資料系統時需要的能力:
這表示開發者不需要從零開始實作 SQL Parser、Optimizer 與 Execution Engine,就可以把 DataFusion 嵌入自己的 Rust 專案中。
這裡需要先區分「資料庫」與「查詢引擎」。
一套完整的資料庫通常還要處理:
DataFusion 的核心定位比較接近一套可以嵌入其他程式的 Query Engine Library,而不是直接提供所有資料庫功能的完整產品。
它主要負責:
SQL / DataFrame
↓
Query Planning
↓
Query Optimization
↓
Query Execution
↓
Arrow RecordBatch
開發者可以在外面加入自己的儲存方式、服務介面與應用邏輯,使用 DataFusion 負責中間的查詢處理。
因此,DataFusion 可以被用來建構:
DataFusion 核心主要在單一 Process 中執行,利用多執行緒進行平行查詢;如果需要分散式執行,也可以透過其他專案或自行建構分散式架構。
DataFusion 使用 Apache Arrow 作為原生的記憶體資料格式。
可以先把三者的關係理解成:
Rust
└── 實作 Query Engine 的程式語言
Apache Arrow
└── 資料在記憶體中的欄式表示方式
Apache DataFusion
└── 使用 Rust 與 Arrow 建立的 Query Engine
資料從 CSV、JSON 或 Parquet 讀進來後,會被轉換成 Arrow 的欄式資料結構,再交給 DataFusion 的不同 Operator 處理。
CSV / JSON / Parquet
↓
Arrow RecordBatch
↓
DataFusion
↓
Filter / Projection / Aggregate / Join
↓
Result
Arrow、Array 與 RecordBatch 會在 Day 7~10 再深入介紹。現在只要先知道,Arrow 是 Rust 與 DataFusion 能夠有效率處理欄式資料的重要基礎。
市面上已經有許多成熟的資料處理工具,例如 Spark、DuckDB 或不同的資料庫系統。
選擇 DataFusion 並不是因為其他工具不好,而是因為這個系列的目標是理解 Query Engine 的內部流程。
DataFusion 適合這個目標的原因包括:
我們可以直接在自己的 Rust 專案加入 DataFusion,不一定要另外啟動一套資料庫服務。
Data Source、UDF、Optimizer Rule、Logical Plan 與 Execution Plan 都提供不同程度的擴充介面,適合觀察 Query Engine 各元件如何合作。
可以從熟悉的 SQL 開始,再逐步觀察 SQL 如何轉換成 Logical Plan、Physical Plan 與最後的執行結果。
CSV、JSON 與 Parquet 都會出現在這個系列中,方便我們比較不同格式進入 Query Engine 後的處理方式。
除了閱讀文件與使用 API,也能透過 Issue、Pull Request、測試與 Code Review,實際了解一個 Query Engine 如何由開源社群共同維護。
這也是我選擇 DataFusion 的重要原因:不只是使用它,也希望能逐漸理解它、閱讀它,甚至參與改善它。
把 Rust 與 DataFusion 放在一起,我們不只是在學一個新的資料處理工具。
我們也會接觸:
Rust
├── 型別系統
├── Ownership
├── Error Handling
└── 非同步與並行處理
DataFusion
├── Data Source
├── Logical Plan
├── Query Optimizer
├── Physical Plan
└── Execution Engine
Apache Arrow
├── Schema
├── Array
├── RecordBatch
└── Columnar Memory Format
這三部分會共同組成接下來 30 天的主線。
今天回答了「為什麼是 Rust × Apache DataFusion」。
Rust 提供了效能、記憶體安全與並行處理的基礎;Apache Arrow 定義了欄式資料在記憶體中的表示方式;DataFusion 則建立在兩者之上,提供 SQL、Query Planning、Optimization 與 Execution 等能力。
可以先用一句話總結:
Rust 負責安全且有效率地實作系統,Arrow 負責表示資料,DataFusion 負責規劃與執行查詢。
明天將正式進入實作:
建立第一個 Rust × DataFusion 專案,使用
SessionContext執行第一段 SQL。