欄式儲存與 OLAP (Column-Oriented Storage)

★★★★Draft

FromDDIA

📚 From the Books

欄式儲存與 OLAP (Column-Oriented Storage)Storage Engines

前面幾頁的 B-Tree / LSM 都是為 OLTP(線上交易)調的:按鍵取少量列、低延遲隨機讀寫。但分析型負載 (OLAP) 剛好相反——掃幾兆列、只碰其中四五個欄、做聚合。存取模式一翻轉,最划算的擺法也跟著翻:不再「一列的欄位擺一起」,而是「一欄的值擺一起」。

🧠 Intuition

TIP

分析查詢通常是「幾兆列 × 只要 4~5 個欄」。按列儲存 (row-oriented) 為了那幾個欄得把整列(上百欄)從磁碟撈進來再丟掉九成——純浪費。欄式儲存 (column-oriented) 把同一欄的所有值連續存成一個檔,查詢只讀用到的那幾個欄檔,I/O 立刻少一個數量級。

⚖️ Tradeoffs

星狀 schema:事實表 + 維度表
  • 資料倉儲常用星狀架構 (star schema):中央是事實表 (fact table),每列是一個事件(如一筆銷售),欄位含屬性(售價、成本)與指向維度表 (dimension table) 的外鍵。
  • 維度表描述事件的 who / what / where / when(產品、商店、日期,連日期都常做成維度表以編入公共假日等資訊)。事實表居中、維度表環繞如星芒,故名。
  • 事實表常有上百個欄;雪花架構 (snowflake schema) 把維度再拆子維度、更正規化,但星狀因對分析師更直觀而更常用。
欄壓縮:bitmap + run-length
  • 同一欄的值高度重複,特別好壓。點陣圖編碼 (bitmap encoding):一個有 n 種相異值的欄,建 n 個點陣圖,每個點陣圖每一位對應一列。n 小時每列只花一位;n 大導致點陣圖稀疏,就再套遊程編碼 (run-length encoding) 壓。
  • 剛好貼合倉儲查詢:WHERE product_sk IN (30,68,69) → 載入三張點陣圖做 bitwise ORWHERE product_sk=31 AND store_sk=3 → 兩張點陣圖做 bitwise AND
  • 壓縮還讓資料更省記憶體→CPU 快取的頻寬:壓緊的欄資料能塞進 L1 快取、在緊密迴圈裡用 SIMD 指令直接對壓縮資料做位元運算——即向量化處理 (vectorized processing)
排序副本:Vertica 讓每份副本排序不同
  • 欄式仍可整體按某欄排序(必須整列一起排以維持欄間對應);管理員依常見查詢挑排序鍵(如先日期再產品),日期範圍查詢就只掃一小段。
  • 排序也助壓縮:排序後第一排序鍵產生大量連續重複值,遊程編碼可壓到極小(幫助在第一排序鍵最顯著、往後遞減)。
  • Vertica 更進一步:資料本來就要複製多份防故障,那就讓每份副本用不同排序,查詢時挑最適合的那份——類似按列儲存的多重次要索引,但欄式沒有指向他處的指標,只有含值的欄本身。
欄式的寫入困難 → 借 LSM 的招
  • 壓縮+排序讓讀極快,卻讓寫很痛:壓過的欄無法像 B-Tree 就地更新,往排序表中間插一列得重寫所有欄檔
  • 解法借 LSM-tree:寫入先進記憶體的排序結構,累積後批次合併到磁碟上的欄檔。查詢得同時看磁碟欄資料與記憶體最新寫入,但查詢優化器對使用者隱藏這個區分——這正是 Vertica 的做法。
物化視圖與 data cube
  • 物化視圖 (materialized view):把查詢結果實際寫一份到磁碟(相對於查詢時才展開的虛擬視圖)。底層資料一變就得更新、增加寫入成本,但在讀取密集的倉儲裡通常划算。
  • 資料立方體 (data cube / OLAP cube) 是物化視圖的特例:按多個維度預先聚合。以日期 × 產品的二維表為例,每格是該組合的聚合值,沿列或欄加總得單維彙總。
  • 取捨:cube 讓特定查詢極快,卻喪失查原始資料的彈性(若「售價」不是維度,就算不出「售價 > 100 的銷售佔比」)。所以多數倉儲盡量保留原始資料,只把 cube 當特定查詢的加速手段。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading