← 返回信息流
AI 资讯Hacker News·1 小时前

TimescaleDB 如何压缩时序数据

原标题:How TimescaleDB compresses time-series data

速览

TimescaleDB 通过先进的压缩技术显著降低时序数据的存储成本。该机制在保持查询性能的同时,有效优化了数据写入效率。这对于处理大规模物联网和监控数据至关重要。

AI 深度解读

TimescaleDB 压缩技术深度解析:Hypercore 引擎与列式存储如何实现 98% 压缩率

背景

在物联网(IoT)和时序数据(Time-series data)领域,数据量往往呈指数级增长。传统的通用数据库压缩算法(如 OLTP 数据库中常用的算法)难以高效处理这类具有特定模式的数据。虽然 PostgreSQL 内置了 TOAST(The Oversized-Attribute Storage Technique,超大属性存储技术)机制,但其设计初衷是解决单个字段值过大(如长字符串、JSONB、Bytea)的问题,通过压缩或拆分数据以适应固定的页大小(通常为 8 kB)。

然而,时序数据的痛点不在于单行数据的体积,而在于跨行的数据模式。TimescaleDB 作为基于 PostgreSQL 构建的时序数据库,引入了名为 Hypercore 的混合行-列存储引擎,旨在解决这一根本不同的问题。Hypercore 能够利用时序数据的特性,实现高达 98% 的压缩率,显著降低长期数据保留的存储成本,并提升分析查询的性能。

核心内容

Hypercore 引擎与列式存储架构

TimescaleDB 的压缩机制核心在于 Hypercore 引擎。这是一个混合的行-列存储引擎,其工作流程如下:

  1. 写入阶段(行式存储):新写入的数据首先以基于行的块(chunks)形式存储在 PostgreSQL 中,以支持快速的 INSERT 和 UPDATE 操作。
  2. 转换阶段(列式压缩):随着数据变旧,这些块会自动转换为列式、压缩后的格式。
  3. 读取阶段(列式扫描):分析性查询读取压缩后的数据时,只需读取必要的字节,从而大幅减少 I/O 并提高查询速度。

与传统行式存储按行顺序存储不同,列式存储按列组织和压缩数据。这意味着查询可以批量获取所需字段,而无需扫描整个行。

数据压缩的具体过程

当对块进行压缩时,系统会将行分组为最多 1000 行的批次(batch)。每个批次在压缩表中变为单行,其列存储为数组。

  • 列主序格式:批次内部采用列主序(column-major)格式,将同一列的值聚集在一起。这使得查询可以选择单个列而无需读取整个批次。
  • 高级压缩技术:每个批次应用多种列级压缩技术,包括游程编码(Run-Length Encoding, RLE)、差值编码(Delta Encoding)和 Gorilla XOR 压缩。

具体压缩算法详解

TimescaleDB 并非使用“一刀切”的压缩算法,而是根据列的数据类型智能选择算法:

  1. 整数、时间戳、布尔值及类整数类型

    • 使用 差值编码(Delta Encoding)差分之差编码(Delta-of-Delta)Simple-8b游程编码(RLE) 的组合。
    • 差值编码:仅存储当前值相对于前一个值的变化量。对于变化微小的数值,这能显著减小存储大小。
    • 差分之差编码:当时序数据具有恒定间隔(如每 5 秒采集一次)且值重复时,差分之差为 0,可用极少的比特位存储。Facebook 的 Gorilla 算法也采用了类似的时间戳处理策略。
    • Simple-8b:将差分之差产生的小数值物理打包,每个值仅需几位比特。
  2. 浮点数(如温度、振动测量)

    • 使用基于 GorillaXOR 压缩,辅以字典压缩。
    • 当相邻浮点数相似时,XOR 运算结果会产生大量前导和尾随零。此时只需存储中间具有意义的比特位,而非完整的 64 位。
  3. JSONB 类型

    • 采用双层策略:首先尝试字典压缩(当值重复时);若无重复,则回退到 PostgreSQL 的 TOAST 机制(默认为 pglz,若配置则使用 lz4)。
  4. 其他类型(字符串、非常规类型)

    • 主要使用 字典压缩(Dictionary Compression)
    • 字典索引本身也会经过 Simple-8b 和 RLE 处理,形成两级压缩。

案例对比

  • 高重复性数据:如 machine_idsensor_type。若 5 行均为 MACHINE_001,RLE 仅存储一次该值及计数器,节省大量空间。
  • 单调递增数据:如时间戳,压缩后几乎为零字节。
  • 高熵数据:如每行唯一的 UUID。由于字典无法有效压缩唯一值,TimescaleDB 会检测此情况并禁用字典压缩,否则压缩效果极差。

关键配置参数:segmentbyorderby

压缩效果极大程度上取决于数据如何被分组。两个关键参数决定了行如何被聚合成批次:

  1. segmentby

    • 指定在批次内共享值的列(如 machine_idsensor_id)。
    • 该列的值在每个批次中仅存储一次,而非数组。
    • 查询优化:查询规划器利用 segmentby 的元数据,可以直接跳过不符合 WHERE 子句的整个批次,无需读取数据本身。
    • 注意事项segmentby 的基数(Cardinality)不能过高。如果每个传感器在块中只有几行,会导致批次填充不足,压缩失效。官方建议每个段在块中至少包含 100 行,最优情况下每个块有 100–10,000 个唯一的 segmentby 值。
  2. orderby

    • 指定批次内部的排序顺序,通常为 time DESC
    • 按时间排序能使差值编码和差分之差编码发挥最大优势,因为相邻值非常接近,差异极小,易于压缩。

配置示例

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

在此配置下,查询 WHERE machine_id = '...' AND time BETWEEN ... 的速度可比未配置 segmentby 时快一个数量级,因为规划器基于元数据跳过了其他机器的数据批次。

关键要点

  • 压缩原理差异:TimescaleDB 压缩针对跨行模式优化,而 PostgreSQL TOAST 仅针对单行大字段优化,两者互补。
  • 混合存储引擎:Hypercore 引擎结合行式写入(快速)和列式存储(高效压缩与分析),实现高达 98% 的压缩率。
  • 算法自适应
    • 数值型/时间戳:Delta + Delta-of-Delta + Simple-8b。
    • 浮点数:Gorilla XOR 压缩。
    • 重复字符串:游程编码(RLE)。
    • 高熵唯一值:禁用字典压缩,避免无效压缩。
  • 配置至关重要
    • orderby 设为时间降序以最大化差值压缩效果。
    • segmentby 用于分组和查询剪枝,但需避免基数过高导致批次稀疏,影响压缩效率。
  • 性能提升:合理的压缩配置不仅节省存储,还能通过减少 I/O 和元数据剪枝显著提升分析查询速度。

意义与影响

TimescaleDB 的压缩技术解决了时序数据长期存储的成本痛点。通过实现高达 98% 的压缩率,企业可以以更低的成本保留更长时间的历史数据,这对于需要长期趋势分析、故障回溯和合规性审计的场景至关重要。

此外,列式存储与智能压缩的结合,使得在 PostgreSQL 生态系统中进行大规模时序数据分析成为可能。它打破了传统关系型数据库在处理海量时序数据时的性能瓶颈,允许用户在不牺牲写入性能的前提下

查看原文 →roszigit.com