TimescaleDB Compression: Hypercore and Columnar Storage

TimescaleDB 透過利用 hypercore 引擎,針對時序數據實現了高達 98% 的壓縮率。與通用型壓縮不同,hypercore 採用了一種混合行-列式方法,利用針對時序數據數學特性(例如單調性和重複性)量身定制的專用演算法。

Hypercore vs. PostgreSQL TOAST

TimescaleDB 的壓縮功能是 PostgreSQL 內建 TOAST (The Oversized-Attribute Storage Technique) 的補充而非替代品。雖然 TOAST 管理超過特定閾值的單個大型值(例如長字串或 JSONB),但 hypercore 優化的是 跨行模式 (cross-row patterns)

Feature TOAST (vanilla PostgreSQL) TimescaleDB hypercore
Design Goal 單個值 > 2 KB 時序數據中的跨行模式
Trigger 行超過 TOAST_TUPLE_THRESHOLD 每個 chunk 的策略 (例如,早於 7 天)
Supported Types 僅限變長類型 (text, jsonb, 等) 所有數據類型
Algorithms pglz, lz4 Delta, Delta-of-Delta, Simple-8b, RLE, XOR-based, Dictionary
Granularity 每個值 每個 batch (~1000 rows)
Data Structure 將值視為不透明的位元組 利用數值結構和重複性
Sensor Floats Ratio ~1.0× 10-20×
Timestamp Ratio ~1.0× 50-100×
Text Ratio 2-3× 5-10×

How Columnar Compression Works

Hypercore 將較舊的數據 chunk 從行式格式(針對快速 INSERTs 優化)轉換為列式格式。在此過程中,行被分組為最多 1,000 行的 batch。每個 batch 在壓縮後的表格中儲存為單一列,其中列 (columns) 以陣列的形式表示。

Specialized Compression Algorithms

TimescaleDB 根據列的數據類型選擇壓縮演算法,以實現效率最大化:

  • Integers, Timestamps, and Booleans: 使用 delta encoding (儲存值之間的差異)、delta-of-delta (儲存差異的變化,對於定期間隔來說為 0)、simple-8b,以及 run-length encoding (RLE)
  • Floats (e.g., temperature/vibration): 採用 XOR-based compression (基於 Gorilla 演算法)。透過對相鄰的浮點數進行 XOR 運算,引擎僅儲存重要的位元,忽略長串的零。
  • JSONB: 使用兩層方法:首先使用重複值的字典 (dictionary);若無重複值,則退回到 PostgreSQL TOAST。
  • Strings and Other Types: 使用 dictionary compression,其中字典索引本身會使用 simple-8b 和 RLE 進一步壓縮。

Example: Delta and Run-Length Encoding

對於在許多行中重複出現的 machine_id,RLE 會將該值儲存一次並附帶一個計數器(例如,MACHINE_001 × 5),而不是重複五次該字串。對於定期間隔的時間戳,delta-of-delta 編碼可以將每個值的儲存需求降低到接近 0 位元組。

Optimizing Compression with segmentby and orderby

兩個參數對於決定如何將行分組為 batch 並有效地進行壓縮至關重要:

  • segmentby: 定義了在一個 batch 中共享的值的列 (column)(例如, machine_id)。該值在每個 batch 中僅儲存一次。查詢規劃器會使用此元數據來跳過不符合 WHERE 子句的整個 batch。
  • orderby: 定義了 batch 內的排序順序(通常為 time DESC)。按時間排序可以最大化 delta 和 delta-of-delta 編碼的效率,因為相鄰的值更有可能相似。

Configuration Example:

ALTER TABLE iot_sensor_data SET (
  timescaledb.orderby = 'time DESC',
  timescaledb.segmentby = 'machine_id'
);

Best Practice: 每個 segment 應包含每個 chunk 的至少 100 行,且每個 chunk 中具有 100–10,000 個唯一的 segmentby 值是最佳範圍。

Impact on Query Performance

對於大多數時序工作負載,壓縮 增加 了查詢速度,透過將 I/O 需求降低 10–20×。

Performance Gains

  • Range scans 隨時間進行的聚合查詢 (SUM, AVG, MAX)。
  • Queries filtering on the segmentby column. 查詢過濾濾器應用於 segmentby 列。
  • Sequential scans 進行大範圍的掃描。

Performance Trade-offs

  • Point lookups 單個行之類的點查詢可能較慢。
  • UPDATE/DELETE 在壓縮後的 chunk 上進行操作需要經過「解壓縮–修改–重新壓縮」的循環。

Real-World Benchmark

在一個使用 MQTT 傳感器數據的生產環境測試中,透過 id 和窄時間範圍進行點讀取,從行存儲 (rowstore) 轉換到列存儲 (columnstore) chunk 時,執行時間展現了 28× 的加速 (10.2 ms 降至 0.36 ms) 且 42.8× 的壓縮率 (308 MB 降至 7.2 MB)。

Why Columnstore is Faster

  1. Sparse MinMax Index: TimescaleDB 自動在 (segmentby_col, _ts_meta_min_1, _ts_meta_max_1) 上建立索引。這允許引擎消除整個 batch,而無需讀取實際數據。
  2. Native Filtering: 因為具有相同 id 的行在物理上被分組,引擎可以立即定位到正確的 segment,而不需要在 (id, time) 上使用大型 B-tree 索引。
  • Vectorized Execution: 運算是在 1,000 行的 batch 中進行處理,而不是逐行處理,從而降低了 CPU 開銷。

Summary

TimescaleDB 使用 Hypercore 引擎,透過將行式 chunk 轉換為使用 delta encoding 和 Gorilla XOR 等專用演算法的混合列式格式,在時序數據上實現了高達 98% 的壓縮率。

Title

TimescaleDB Compression: Hypercore and Columnar Storage

Sources