← 返回信息流
AI 资讯Hacker News·2 天前

TimescaleDB将数据块大小从30天缩减至7天

原标题:We shrank our TimescaleDB chunks from 30 days to 7

速览

TimescaleDB近期对其底层数据存储机制进行了优化,将默认的数据块(chunk)大小从30天缩减至7天。这一调整旨在提高查询效率并优化存储管理,特别是在处理高频写入场景时表现更佳。对于依赖时序数据库的企业用户而言,这意味着更精细的数据粒度和潜在的性能提升。

AI 深度解读

深度解读:为何我们将 TimescaleDB 的数据块(Chunks)从 30 天缩小到 7 天

来源:Hacker News / Sodatone Engineering 作者:Yask Srivastava 背景:Sodatone 是 WMG(华纳音乐集团)旗下的 A&R(艺人与曲目部)智能平台,负责从流媒体和社交媒体平台抓取用户互动信号,将其转化为时间序列数据,供内部侦察员和唱片团队发掘新兴艺人。

背景

在 Sodatone 平台中,绝大多数数据存储在 TimescaleDB 的超表(Hypertables)中,每个“平台-指标”对对应一个超表。由于数据量巨大且增长迅速,超表的健康状况往往预示着整个系统的稳定性。

TimescaleDB 的底层架构基于 PostgreSQL。虽然对用户而言,超表看起来像是一个单一的表,但在底层,它是由许多较小的表——即数据块(Chunks)——组成的集合。每个数据块存储特定时间范围内的行数据。例如,如果一个超表的数据块间隔设置为 30 天,且存储了一年的数据,那么它在物理上实际上是 12 个表的拼接。

这种架构的核心优势在于查询剪枝(Chunk Pruning):当查询最近一个月的数据时,查询规划器只会访问最新的那个数据块,而跳过其他所有数据块。这极大地提高了查询速度。然而,随着数据摄入率的增加,原本合理的配置可能变得不再适用。

核心内容

为什么数据块大小至关重要?

数据块的大小直接影响五个关键性能指标,且这些影响是累积性的:

  1. 内存中的工作集(Working Set in Memory):活跃(未压缩)的数据块是热点写入和近期数据读取的主要目标。如果活跃数据块无法舒适地放入共享缓冲区(shared buffers)和页面缓存(page cache)中,每次近期查询都将不得不支付昂贵的 I/O 成本。
  2. 查询剪枝效率(Chunk Pruning):查询规划器会跳过时间范围与 WHERE 谓词不重叠的数据块。这是超表在时间范围扫描中保持快速的主要原因。较小的数据块使得针对近期数据的查询剪枝更加精准。
  3. 压缩批次大小(Compression Batch Size):TimescaleDB 的压缩策略会在数据块超过配置的年龄后对其进行压缩。较大的数据块在压缩和解压缩时比小数据块耗时更长。
  4. 回填成本(Backfill Cost):当需要向已压缩的数据块重新插入数据时,系统必须将其解压、应用更改,然后重新压缩。数据块是这一操作的基本单位。
  5. 保留粒度(Retention Granularity):如果应用了 add_retention_policy,数据块也是数据驱逐(eviction)的基本单位。

TimescaleDB 官方建议,活跃数据块的大小应大致占可用内存的 25%。这是一个动态目标。随着摄入率的增长,相同的时间间隔代表更多的字节数。一年前设置合理的 30 天数据块,如今可能已成为性能瓶颈。

关键机制:set_chunk_time_interval

需要特别注意的是,set_chunk_time_interval 命令仅影响未来的数据块。现有的数据块保持其原始大小,并继续正常查询。该操作无需重写、无需独占锁、也无需回填数据。超表会在下一个数据块边界到达时自然过渡。这使得它成为 TimescaleDB 中风险最低的调整参数之一。如果结果不理想,可以以相同的方式撤销。

Sodatone 遇到的问题

去年年底,Sodatone 团队发现其中一个负载较重的超表(每周数百万行,压缩前磁盘占用达数 TB)开始出现老化迹象:

  • 压缩滞后:压缩作业跟不上数据摄入速度。
  • 读取变慢:随着秋季的到来,近期数据读取逐渐变得沉重。
  • 回填代价高昂:当上游 feed 重新发布几天历史记录时(这种情况比预期更频繁),系统需要解压整整一个月的数据来吸收更改。

当初设置该表为 30 天数据块时,数据量尚小,但如今这一配置已不再适用。

实施的变更

今年 9 月,该表的压缩作业开始失败,因为单个数据块太大,无法在一次运行中完成压缩。这是团队着手解决的首要问题。

  1. 初步调整:将该表的数据块间隔从 1 个月调整为 7 天。通过 Timescale Cloud 监控面板观察,作业恢复正常。
  2. 推广修复:两个月后,另一个音乐排行榜数据 feed 出现了相同的失败。同样的修复措施奏效。鉴于两个月内发生了两次,团队在 12 月初通过一个 Pull Request(PR)将其余所有热点平台互动表的数据块统一调整为 7 天。

迁移代码示例(Ruby on Rails ActiveRecord):

class ShrinkChunks < ActiveRecord::Migration[6.1]
  def up
    safety_assured do
      TimescaleRecord.set_chunk_time_interval(
        "hypertable", interval: "7 days"
      )
    end
  end

  def down
    safety_assured do
      TimescaleRecord.set_chunk_time_interval(
        "hypertable", interval: "30 days"
      )
    end
  end
end

取得的成效

  • 压缩速度加快:较小的数据块使压缩策略运行得更快,缩短了“实时数据”与“压缩数据”之间的差距。
  • 回填成本降低:当上游 feed 重新发布一周的历史记录时,现在只需解压一个 7 天的数据块,而不是拖拽整个月进行重写。
  • 近期读取保持轻量:针对“过去 24 小时”或“过去 7 天”的查询最多只跨越一两个数据块,查询剪枝按预期工作。
  • 保留粒度更细:虽然团队目前尚未在这些表上应用数据保留策略,但未来如果需要,现在已具备更精细的操作选项,无需进行大规模手术。

需要注意的风险

数据块增多意味着 TimescaleDB 目录中的行数增加,以及针对极宽时间范围(如扫描一年或更久数据)的查询在规划阶段的工作量增加。

团队在每次变更后,重新运行了最宽泛的查询,并在随后的一周和月底再次验证,未发现问题。但结论并非“永远使用 7 天”。正确的做法是根据当前的摄入率,确保活跃数据块能舒适地驻留在内存中。对于数据量较小的表,30 天可能仍然是正确的选择。

关键要点

  • 动态调整必要性:数据块大小不应是一成不变的。随着数据摄入率的增长,原本合适的配置(如 30 天)可能变得过大,导致 I/O 压力增加和压缩延迟。
  • 安全且可逆的操作:使用 set_chunk_time_interval 调整数据块大小仅影响未来数据,无需锁表、无需重写现有数据,且操作完全可逆。
  • 内存占用法则:TimescaleDB 的最佳实践是保持活跃数据块大小约为可用内存的 25%。这是一个需要定期 Sanity Check(合理性检查)的指标。
  • 性能权衡:更小的数据块能提升近期查询效率和压缩速度,但会增加元数据开销和极宽时间范围查询的规划成本。需根据实际查询模式(近期热点 vs 长期汇总)进行权衡。
  • 自动化监控预警:当压缩作业开始失败或近期读取性能显著下降时,往往是数据块过大需要调整的明确信号。

意义与影响

对于任何大规模使用 TimescaleDB 处理时间序列数据的团队而言,这篇文章提供了一个极具价值的运维视角:配置即代码,且需随业务增长而演进。

  1. 运维最佳实践的普及:许多用户可能认为创建超表后,初始配置便一劳永逸。本文强调了“定期回顾”的重要性,特别是对于运行超过一年的生产环境超表。
  2. 降低运维风险:通过展示一个只需一行代码、无需停机、完全可逆的优化手段,降低了 DBA 和工程师调整底层存储结构的心理门槛和技术风险。
  3. 成本与性能的平衡:通过优化数据块大小,Sodatone 不仅提升了
查看原文 →tech.wmg.com