資料倉儲
想像你經營一家小型網路書店。每當有顧客註冊、把書加入購物車,或完成付款,你的應用程式就會往資料庫寫入一列。那個資料庫只為一件事而生:每秒處理數千筆又小又快的動作——讀取某一位顧客、更新某一筆訂單、插入某一筆付款。我們把這類系統稱為 OLTP(線上交易處理,online transaction processing)。它是產品活生生、不斷跳動的心臟。
現在你的主管問了另一種問題:「上一季的營收是多少?請依書籍類別和地區拆開來看。」要回答這個問題,你需要的不是某一列,而是要橫掃數百萬筆訂單,把它們分組再加總。如果你拿這個沉重的查詢去打那個服務應用程式的資料庫,報表還在慢慢算的同時,真實顧客的結帳就會被拖慢。這兩件工作——服務應用程式,以及回答分析性問題——是往相反方向拉的。
解法是另外架一個專為第二件工作而生的資料庫:資料倉儲。倉儲是為 OLAP(線上分析處理,online analytical processing) 最佳化的——在這種場景裡,查詢會讀取許多列的大片切面並加以彙總。為了又快又省,多數雲端倉儲採用欄式儲存(columnar storage):它不是把每一列的資料黏在一起放到硬碟,而是把每一欄的資料放在一起。如此一來,「依類別計算營收」的查詢就只需讀取它真正用到的那兩三欄,其餘略過。Snowflake、Google BigQuery、Amazon Redshift 與 Databricks SQL 在這個意義上都是倉儲。
為什麼欄式排列對「成本」和「速度」都這麼關鍵?許多雲端倉儲——無論直接或間接——是依查詢讀取了多少資料來計費。因此一個很好用的經驗法則是:一次查詢的成本,大致與它掃描的位元組數成正比。讀的欄位少,掃描的位元組就少;既付得少,也跑得快。
這是一個心智模型,不是精確的帳單:掃描更少的欄位、更少的列(透過分區與篩選),是讓倉儲查詢更便宜、更快的主要方法。
資料湖——以及湖倉
倉儲偏愛欄位明確、整整齊齊的表格。但許多真實資料並不整齊:以 JSON 形式存在的原始點擊流日誌、應用程式的事件區塊、感測器讀數、PDF、影像、音訊。更麻煩的是,資料剛進來時,你往往還不知道日後會怎麼用它——所以一開始就鎖定一個固定的表格形狀,會顯得太早。在第一天就把這一切硬塞進倉儲表格,既彆扭,有時甚至辦不到。
資料湖解決了這個問題。資料湖是便宜的物件儲存(object storage)——想想 Amazon S3 或 Google Cloud Storage——它以資料原本的形狀存放原始檔案:CSV、JSON、Parquet、影像,什麼都行。它和倉儲最關鍵的差別在於「你何時為資料加上結構」。倉儲採寫入時定義綱要(schema-on-write):你必須先定義好欄位與型別,才能載入資料。資料湖採讀取時定義綱要(schema-on-read):你現在先把原始檔案倒進去,等到日後查詢的那一刻,才決定要怎麼把它們解讀成欄位。(順帶一提,Parquet 是資料湖常用、用來維持速度的一種欄式檔案格式。)
多年來,團隊覺得自己被迫二選一:要嘛是倉儲那可靠的表格與快速的 SQL,要嘛是資料湖那便宜、靈活的儲存。湖倉(lakehouse)就是想一次兼得兩者的嘗試。它建立在 Delta Lake、Apache Iceberg 這類開放表格格式之上,把資料以便宜的檔案形式留在物件儲存裡,卻又補上了讓倉儲值得信賴的那些東西:真正的表格、交易(transactions),以及上層的 SQL。同一個地方,既服務雜亂的原始資料,也服務乾淨的分析表格。
用一個簡單的方式記住這三者:倉儲像一間整齊、標示清楚的儲藏室,東西都已分裝進罐子裡;資料湖像一座又大又便宜的車庫,你把原始食材一股腦丟進去;湖倉則是一座學會在自己裡頭擺出一層貼了標籤的儲藏架的車庫。多數分析工作,仍然是針對倉儲式的表格在跑——無論那些表格住在倉儲裡,還是湖倉裡。
從 ETL 到 ELT,以及它為何翻轉
原始資料幾乎從不會以你分析所需的形狀抵達。它必須被搬移、被重塑,而這件事有一道經典的三步驟食譜,正是 ETL/ELT 這個詞所概括的。三個步驟是:萃取(Extract,把資料從各個來源系統拉出來——應用程式資料庫、金流商、廣告平台)、轉換(Transform,清理、修正型別、改名、合併,重塑成整齊的分析表格),以及載入(Load,把成果放進倉儲)。唯一的問題,是後面兩個字母的順序。
在舊世界裡,這道食譜是 ETL:先萃取,接著在另一台處理伺服器上轉換,最後只把整理好、打磨完成的表格載入倉儲。為什麼要先轉換?因為舊式倉儲又貴又不夠強,所以你把沉重的重塑工作放到外面做,只把瘦身後的最終結果存進去。它的缺點是:一旦業務問題改變,你往往得從頭重建管線,因為原始的細節早就被丟掉了。
雲端改變了成本結構,順序於是翻轉成 ELT:先萃取,把原始資料先載入倉儲,然後在倉儲裡用 SQL 進行轉換。現代雲端倉儲是大規模平行運算,儲存又便宜,所以如今要保留原始資料、並就地重塑,完全負擔得起。最大的好處是:因為你從不丟棄原始資料,當問題改變時,你只要針對同一批原始表格寫一段新的轉換——不必再回到來源重新萃取。
一張流程圖:左側數個來源系統匯入一個 Extract(萃取)步驟;一個箭頭把原始資料經由 Load(載入)步驟送進中央的倉儲圓柱;倉儲內部的 Transform(轉換)步驟把原始表格變成乾淨的表格,再往右供應儀表板與模型。
SQL:依然是通用語
如果有一項技能能把這整個層次串起來,那就是 SQL——結構化查詢語言(Structured Query Language),向表格提問的語言。SQL 自 1970 年代就存在,並且不斷熬過每一次「它要死了」的預言,原因很簡單:它是宣告式(declarative)的。你描述你想要什麼結果,倉儲的引擎自會想辦法有效率地算出來。你說「把這些訂單依類別分組並加總營收」;你不必說要怎麼掃描硬碟、或怎麼把工作切給多台機器。
下面就是最開頭那個「依類別與地區計算營收」的問題,寫成針對一張乾淨倉儲表格的真實 SQL。把它當成一個句子來讀:選出這些欄位、來自這張表格、條件是日期夠新、依這樣分組、再按營收排序。
SELECT category, region, SUM(amount) AS revenue, COUNT(*) AS orders FROM warehouse.fct_orders WHERE order_date >= '2026-01-01' GROUP BY category, region ORDER BY revenue DESC;
那個 GROUP BY,就是你也許在資料框(Python 的 pandas、R 的 dplyr)裡見過的分組彙總。它是分割-套用-合併模式的 SQL 拼法。SQL 之所以是通用語,是因為人人都會說:分析師、資料工程師,甚至多數商業智慧工具(儀表板工具)在底層也都在產生 SQL。學會一次,你就能直接和倉儲對話,無論它掛的是哪家廠商的標誌。
dbt 與分析工程
一旦 ELT 把轉換搬進倉儲,這份轉換就會膨脹成一大片 SQL:幾十段用其他表格建出新表格的腳本。靠手動執行、順序又錯、邏輯還在檔案之間複製貼上,這很快就會變成一團脆弱的爛攤子。同一個「活躍顧客」的定義,最後被寫成五種略有出入的版本,沒人說得準哪個儀表板才對。
dbt(即「data build tool」,資料建構工具)把軟體工程的習慣帶進 SQL,藉此馴服這團亂。你把每一段轉換寫成一個模型(model)——其實就是一段存成檔案的 SELECT 敘述——並用一個 ref 函式去引用其他模型,而不是把表格名稱寫死。dbt 會從這些引用自動推算出相依順序,依正確的次序把一切建好。它還會跑資料測試(例如「這個 id 欄位必須唯一且永不為空」)、產生文件,並和你的程式碼一起被納入版本控制。
-- models/marts/fct_orders.sql
SELECT
o.order_id,
o.customer_id,
c.region,
o.amount,
o.order_date
FROM {{ ref('stg_orders') }} AS o
LEFT JOIN {{ ref('stg_customers') }} AS c
ON o.customer_id = c.customer_id團隊通常會把模型分層:暫存層(staging)模型對每個原始來源做輕度清理(改欄名、修型別),而資料市集(marts)把它們合併、彙總成分析師真正會查詢、面向業務的表格。這其實就是把可重現、有腳本的清理套用到整個倉儲,產出的正是公司其餘部門所倚賴、那種整齊又可信的表格。
排程編排:讓管線按時運行
現在我們手上已有一條資料管線的所有零件——從來源萃取、載入倉儲、用 dbt 轉換、刷新儀表板。但這些步驟有相依關係,也有時鐘:萃取必須先完成,轉換才能開始;轉換完成,儀表板才能刷新;而整件事最好每天清晨兩點跑一次,好讓團隊登入時,最新數字已經在等著。必須有個東西,能依正確順序、按時執行這些步驟,並在某一步失敗時妥善應對。
那個東西就是編排器(orchestrator)——排程編排工具,例如 Apache Airflow、Dagster 與 Prefect。你把管線描述成一張 DAG(有向無環圖,directed acyclic graph)——它其實就是一張用箭頭把步驟連起來、且沒有迴圈的圖,所以順序永遠是明確的。編排器接著依排程執行這張 DAG,只在某步所相依的步驟都成功後才啟動它,對暫時性的失敗自動重試,並在真的出問題時通知人類。
一張水平的管線圖:代表各階段的方框由箭頭從左到右串連,每個方框都是一個步驟,唯有前一個方框成功完成後才會開始,最後通往一個對外服務的產物。
一個良好的、被編排的步驟應是冪等(idempotent)的:跑兩次和跑一次得到一樣的結果,這樣在「做了一半才失敗」之後重試,才不會重複計數或弄壞資料。冪等性、重試與告警,正是把一堆脆弱腳本,變成一條你能安心睡過整夜、信得過的管線的關鍵。
不被炒作牽著走地選工具
現代資料技術棧是一場標誌滿天飛的嘉年華,而時髦的答案每年都在變。你很容易覺得:在回答出任何一個問題之前,自己非得先導入湖倉、串流、特徵庫,外加三套編排器不可。其實不必。誠實的真相是:多數公司並不是 Google,而一套託管倉儲,加上 dbt,再加上一個 BI 工具,就能回答一間正常公司絕大多數的業務問題。
所以,用原則而非新聞稿來評斷工具。下面是一份冷靜的清單,幫你挑出你真正需要的東西。
- 從決策出發,而不是從工具出發。先寫下你需要回答的問題、以及誰會依它行動;讓問題去決定技術棧,而不是反過來。
- 選能用的最小技術棧。一個雲端倉儲加上 SQL 就能應付多數需求。只有當你真的有大量、暫時塞不進表格的非結構化資料時,才加上資料湖。
- 偏好無聊但支援良好的工具。一個被廣泛使用、文件齊全、社群龐大的工具,帶給你的痛苦會比一個小眾聰明的工具少,即使後者在展示時更亮眼。
- 從第一天起就要求可重現性與版本控制。如果你無法從頭重跑你的管線並得到相同結果,你就無法信任它——無論它掛的是什麼牌子。
- 只往前多計畫一步,而不是十步。買下足以成長到明年的空間,而不是一個為你也許永遠到不了的規模而打造的平台。
現在你已握有「資料住在哪裡、又如何被重塑」的地圖:用倉儲存放整齊的分析表格、用資料湖(與湖倉)做便宜又靈活的儲存、用 ELT 在倉儲內部轉換、用 SQL 作為共同語言、用 dbt 讓轉換保持誠實,再用排程編排可靠地把這一切跑起來。下一篇指南將把這個循環閉合——從一次性的分析,走向人們信任的儀表板,以及在生產環境運行的機器學習。