資料要存在哪:儲存的階層與抽象,讀《Fundamentals of Data Engineering》Ch.6

· tech

#data-engineering#book-notes

上一篇講資料從源頭生出來。生出來之後第一件事就是:存去哪? 這章談儲存 —— 而它最反直覺的一點是:儲存不是「一個東西」,而是一整條從奈秒到小時、從天價到白菜價的階層。 看懂這條階層,後面所有「該放哪、放多久、花多少」的決定才有依據。

儲存是一條階層,不是一個選項

書從最底層的物理材料往上疊:CPU 快取、RAM、SSD、HDD、物件儲存、冷儲存。它們的差別不是「好壞」,而是同一組取捨的不同落點 —— 越快的越貴、容量越小;越便宜的越慢、容量越大。中間單價和延遲各差好幾個數量級

↑ 越上面:越快、越貴、容量越小 CPU 快取~1 ns RAM 記憶體~100 ns · 揮發 SSD~0.1 ms HDD 磁碟(轉盤)~10 ms 物件儲存(S3 / GCS)~100 ms · 超便宜 封存 / 冷儲存分鐘~小時 · 最便宜 ↓ 越下面:越慢、越便宜、容量越大
儲存是一條速度↔成本的階層,單價與延遲各差好幾個數量級;資料工程的重心落在最便宜、幾乎無限的物件儲存(藍)

這條階層也是快取(cache)的原理:把熱資料往上搬(快、貴),冷資料留在下面(慢、便宜)。Spark 把資料 cache 進記憶體、資料庫用 RAM 當 buffer,都是同一招 —— 拿貴的空間換速度。

對資料工程最重要的一層:物件儲存

上層那些(RAM、SSD)多半被資料庫、引擎藏在底下,你不太直接碰。DE 真正天天面對的是系統層的儲存,快速對照三種:

型態像什麼典型用途
檔案儲存一般檔案系統、資料夾開發、小量、掛載
區塊儲存一顆裸硬碟(EBS)資料庫 / VM 的底層
物件儲存一個超大 key-value 倉庫海量非結構化資料

三者裡的主角是物件儲存(S3、GCS、Azure Blob),現代資料湖幾乎都建在它上面,書也花最多篇幅在它。它憑什麼?幾乎無限擴充、按用量計費超便宜、物件不可變(適合當可重播的原始層),而且天生與運算分離。 最後這點,牽出這章最大的主題。

這章最大的主題:運算與儲存分離

傳統資料庫或 Hadoop,運算和儲存綁在同一台機器上 —— 想加算力,連硬碟一起加;想加容量,連 CPU 一起加,兩邊永遠一起伸縮,結果常常一邊爆滿、一邊閒置。雲時代把它拆開了:

傳統:compute 與 storage 綁死 Compute Disk Compute Disk 加算力得連硬碟一起加、 加容量得連 CPU 一起加 → 綁死、常常一邊浪費 現代:compute / storage 分離 Spark SQL 引擎 臨時作業 物件儲存(共用・持久)便宜、幾乎無限 各自獨立伸縮 · 按需開關 · 用完即走
運算與儲存分離是雲時代最大的轉變:資料躺在便宜持久的物件儲存,運算引擎按需長出來、算完關掉,兩邊互不綁死

這一拆,解釋了近十年一堆架構為什麼長這樣:資料一份放在物件儲存,要跑 Spark 就開一批 executor pod、要下 SQL 就叫一個查詢引擎,算完全部關掉、只留資料。你不再為「尖峰時要多少算力」養一整年的機器,也不再因為資料變多就被迫加 CPU。Spark 那套 driver/executor 按需伸縮、lakehouse、serverless 查詢,地基全是這一條。

但別誤會:綁在一起有時才是對的

分離贏的是「彈性、成本、湖」這類工作負載,不是全部。MPP 資料庫(Greenplum、Teradata、傳統 Redshift)刻意把運算和儲存綁死,而且綁得有道理:每個 segment 節點用本機磁碟存自己那片資料、就地運算,靠 distribution key 讓 join 在本機發生、不用跨網路重分佈。它換到的是分離模型給不了的穩定低延遲與高併發 —— BI dashboard、有 SLA 的查詢、大量複雜 join,本機碟加深度整合的 optimizer 很難被打敗。

綁在一起(MPP)分離(物件儲存 + 彈性運算)
最佳化穩定低延遲、高併發彈性、閒置成本、獨立伸縮
資料在地性天生就有靠快取補回來
痛點關不掉、加節點要重分佈、冷資料佔著貴運算網路延遲、要靠快取續命

而且界線正在靠快取收斂:分離陣營用本機 NVMe 快取熱資料(Snowflake local cache、Databricks disk cache),把在地性事後補回來。大家真正在搶的都是資料在地性——MPP 天生擁有,lakehouse 事後補到。實務上更常見的是兩個都用:湖放海量原始/冷資料,MPP 或倉儲當高併發的服務層,正好對上下一節的抽象分層與資料溫度。

三種資料工程的儲存抽象

在物件儲存這塊地基之上,DE 面對的是三個高層抽象。它們的差別在 Medallion 那篇也碰過,這裡用一張表釘清楚:

抽象一句話schema 時機
資料倉儲(Warehouse)結構化、為分析最佳化schema-on-write(進來就得對格式)
資料湖(Lake)什麼都先丟進來的原始池schema-on-read(讀的時候才套結構)
資料湖倉(Lakehouse)湖的彈性 + 倉的管理兩者合流(在湖上加 ACID、schema)

演進的方向很清楚:湖太亂(什麼都丟、難治理)、倉太硬(貴、只吃結構化),lakehouse 想兩邊的好處都要 —— 在便宜的物件儲存上,補回交易保證與結構管理。

資料是有溫度的:冷熱分層與保存期限

最後一個實用概念:資料溫度。不是所有資料都該放在同一層。

  • 熱(hot):常被查,放快的層(SSD / 記憶體 / 標準物件儲存),貴但快。
  • 溫(warm):偶爾查,放便宜一點的層。
  • 冷(cold):幾乎不查、但為了合規或備份得留,丟進封存(冷儲存),最便宜、取用最慢。

搭配的是保存期限(retention):資料不是存了就永遠留著,而該有生命週期 —— 什麼時候降溫、什麼時候刪。這既是省錢(別讓冷資料佔著貴的層),也是合規(該刪的要刪)。跟 Kafka 的 retention / compaction是同一種思維:儲存要主動管理,不是無限堆積。

反思

「運算與儲存分離」讓我學會的,其實是看工作負載選邊

這章把一個我天天在用、卻沒歸納成一句話的東西講白了。以前資源就是一台台「CPU 配硬碟」的機器,一起買、一起閒置;分離之後,資料放物件儲存、運算按需長出來,成本結構整個變了 —— 沒作業時幾乎不花運算錢,尖峰時才把 executor 開滿。我寫 Spark 部署Spark on K8s 那兩篇反覆講的「用完即刪」,追根究柢就是這條原理的落地。

但我一度把它當成「進步 vs 落後」,是被一個反例修正的:很多人用 Greenplum,還是把運算放進 DB,而且是對的。高併發、要穩定低延遲的服務型查詢,MPP 那種「資料就在本機、就地運算」反而更快 —— 綁在一起不是守舊,是為在地性做的取捨。所以我現在的判斷不是「預設分離」,而是先看工作負載:彈性、成本敏感、冷資料多 → 分離;高併發、要穩延遲 → 綁在一起(或分離再靠快取把在地性補回來)。這跟 Ch.4「先架構後工具、每個選擇都是取捨」是同一句話 —— 分離是強大的預設,不是萬用的答案。

我押注的儲存地基,還是物件儲存

回頭看 Ch.4講的「賭在不變的地基上」,儲存這層我下的注很一致:原始資料一律先落物件儲存,不可變、可重播,再讓上面的引擎自由來去。這跟 Medallion 守著 Bronze 不可變是同一個決定的兩面。物件儲存便宜到「先存了再說」幾乎不痛,而它十年後大概還在 —— 把最重要的資產放在最穩、最便宜的一層,是我越做越安心的選擇。

資料溫度這件事,我是被帳單教會的

「冷熱分層」聽起來很基本,但我真正開始認真管,是被雲端儲存帳單嚇到之後。一堆一年被查不到一次的舊資料,躺在標準層按天燒錢。後來把生命週期規則設起來(自動降溫、自動封存、過期清掉),帳單立刻瘦一圈。教訓是:儲存的成本不在「存」,在「忘了管」。 現在我建任何資料層,retention 與分層規則會跟資料本身一起設計,而不是等帳單爆了才回頭補 —— 這跟我對 工具的態度一樣,主動設計取捨,別讓預設值替你決定成本。