惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
S
Securelist
T
Threat Research - Cisco Blogs
Forbes - Security
Forbes - Security
AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Attack and Defense Labs
Attack and Defense Labs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
W
WeLiveSecurity
P
Palo Alto Networks Blog
O
OpenAI News
V2EX - 技术
V2EX - 技术
H
Heimdal Security Blog
L
Lohrmann on Cybersecurity
NISL@THU
NISL@THU
T
Tor Project blog
T
Threatpost
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Latest news
Latest news
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
量子位
L
LINUX DO - 最新话题
Webroot Blog
Webroot Blog
F
Fortinet All Blogs
C
CERT Recently Published Vulnerability Notes
Hacker News: Ask HN
Hacker News: Ask HN
U
Unit 42
T
Tailwind CSS Blog
WordPress大学
WordPress大学
N
News | PayPal Newsroom
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
Netflix TechBlog - Medium
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Stack Overflow Blog
Stack Overflow Blog
Cloudbric
Cloudbric
C
Check Point Blog
V
Visual Studio Blog
The GitHub Blog
The GitHub Blog
The Hacker News
The Hacker News
B
Blog

InfoQ - 促进软件开发领域知识与创新的传播

Meta 收购 Manus 这事儿泡汤了 5.5万 Star 开源项目 Ghostty 被迫出走,GitHub 正在终结一代技术人的乌托邦 Slack 长时运行多智能体系统的上下文管理方案 从 T+1 到分钟级:金城银行基于 Apache Doris 构建高可靠、强一致的实时数据平台 谷歌云推出 Agents CLI,简化 AI 智能体开发全流程 Claude官方击穿高薪、高学历的安全防线!Anthropic点名10大高危职业,但有群人暂时稳了 亚马逊云科技终止 WorkMail 服务,并将 App Runner 转入维护模式 OPPO小布记忆:全模态碎片化内容的理解与智能整理实践|AICon上海 模力工场038周AI应用周榜:工具在消失,工作流在出现 Akamai CEO Tom Leighton:Agent 时代来临,云基础设施正从“中心化”转向“分布式边缘” 日均数百亿入库背后:从“人肉调度”到K8s弹性架构,度小满金融基于OceanBase重构入库架构实践 百度文库网盘发布GenFlow 4.0:月活用户超1亿,要把网盘变成全端AI工作台 Altman 投的 Agent 终端 Warp 开源了!斩获3.5万star 哪些客户需要拒, 敢让龙虾决定吗?_AI&大模型_InfoQ 中文站_InfoQ精选视频 从开发到生产:为什么越来越多的机器学习团队纷纷迁移到 Snowflake | BUILD 2025_AI&大模型_王玮_InfoQ精选视频 探索多智能体工作流:LangGraph Snowflake Cortex AI | BUILD 2025_AI&大模型_王玮_InfoQ精选视频 腾讯云分布式缓存数据库:AI Agent - 从提示词工程到 Harness 工程 | 腾讯云数据库 DBTalk_腾讯_凌敏_InfoQ精选视频 基于 Streamlit 为 CSV 数据构建分析智能体 | BUILD 2025_AI&大模型_王玮_InfoQ精选视频 AI 智能体:告别文档缺漏 | BUILD 2025_AI&大模型_王玮_InfoQ精选视频 构建 AI 驱动的数据管道:深度探讨 Snowflake Openflow 与非结构化数据 | BUILD 2025_AI&大模型_王玮_InfoQ精选视频 云端太贵、本地不够聪明,英特尔押注“端云混合AI”:智能体PC会替人完成工作 不到10%的存储投入,可能拖垮90%的GPU投资!IBM把AI Agent塞进存储系统,算清企业最容易忽略的一笔账 Snowpark 上手实战 | BUILD 2025_大数据_王玮_InfoQ精选视频 ClickHouse + Langfuse,构建 Agent 可观测基石 腾讯云分布式缓存数据库:Cluster Proxy 共享连接架构深度解析 | 腾讯云数据库 DBTalk_腾讯_凌敏_InfoQ精选视频 AI 写代码太烧钱了:Copilot、Claude 一起涨价,不如把程序员请回来? 英特尔发布至强600系列工作站处理器与锐炫Pro B70 GPU,全新AI工作站来了 腾讯云分布式缓存数据库:从 Redis 到 Valkey - 开源社区如何快速创新 | 腾讯云数据库 DBTalk_腾讯_凌敏_InfoQ精选视频 印奇这次要“从0重做”智驾模型!首谈阶跃和千里双公司布局:中国AI商业闭环要靠车跑出来 从Cursor返聘归来,90后华裔女高管带Claude开启日更模式:token成本比工程师工资低多了! 从 Coding 到 Agent:QCon 北京 2026 全景复盘,优秀出品人 & 明星讲师名单揭晓 全链路支撑大模型国产化“Day 0适配”,商汤大装置构建全栈能力底座 凌晨,OpenAI 与亚马逊云科技史上最大联合发布来了 HashiCorp Vault 2.0 发布:引入新身份联邦机制,迈入 IBM 生命周期体系 Yelp 实现超 1,000 个 Cassandra 节点零停机升级 写了 17 年开源代码,我为什么认为 Coding Agents 堆功能是在瞎折腾? 基于 Apache Camel 编排智能体与多模态 AI 管道 面向智能体与人类用户的AI记忆系统:架构设计与核心场景实践|AICon上海 Anthropic 推出 Managed Agents,简化 AI 代理部署流程 阿里HappyHorse开启灰测,720P视频生成低至0.44元/秒 讯飞联合清华团队押注量子AI:不看营收、不设KPI,一群“无人区”科学家,抢夺下代AI算力入口 小米万亿模型全面开源:MIT 协议、1M 上下文,但还是打不过 DeepSeek Cortex Code 入门指南:面向数据工程师的实践路径 | 技术实践 openJiuwen社区首发Team Skills,定义Coordination Engineering新范式 用 Snowflake Cortex Agents 释放结构化数据的最大价值 | 技术实践 Grafana 利用 Kafka 对 Loki 进行了架构重构,并发布了一款命令行工具,旨在将可观测性引入编码代理 ClickHouse重构全文索引:对象存储上跑出高性能 Full-Text Search 可观测性和遥测技术如何提升软件工程实践 Dropbox 与 GitHub 合作,将单体库大小从 87GB 缩减至 20GB Agent 的下一站:基于长期记忆系统 EverOS 的自我演进|AICon上海 同一赛道,四种收费:Agent 控制层(Harness)开始分裂 Cloudflare Sandboxes 正式发布,为 AI 代理提供持久化隔离环境 Agent 的“记忆断片”困局,该怎么破?_AI&大模型_AICon 全球人工智能开发与应用大会_InfoQ精选视频 数据分析师如何快速建立在 AI 时代最值钱的能力:一份可落地的行动路线图 摩尔线程最新财报:研发占比超86%,万卡级大规模智算集群落地 当云区域失效:地缘动荡环境下的高可用重构 Slack 重构通知系统,设置参与度提升 5 倍 智能体工程的隐性技术债务 “我把所有模型都换成了DeepSeek V4”:月账单将降 90%,效果还更好 阿里云智能集团高级技术专家刘少伟已确认出席AICon上海站,并分享如何构建企业 Agent 的自动化行动架构 构建生产就绪的 tRPC API:Apollo Federation 的 TypeScript 替代方案 Anthropic推出面向Claude Code的基于智能体的代码审查功能 北京车展直击:斑马智能甩出车载Agent短剧,比亚迪率先落地,AI让智能座舱又热起来了 Snowflake 作为智能体运行时:从静态管道迈向自主数据系统 | 技术实践 Snowflake 上的本体体系:基于 Cortex Code 能力实现从架构到部署 | 技术实践 Cloudflare 公布 MCP 架构方案,应对企业面临的安全与治理风险 复杂的项目管理怎么做到「AI 友好」?飞书项目用「开放」给出答案 Snowflake Cortex Code 的规范驱动开发:将 SDLC 方法论引入 AI 辅助工作流 | 技术实践 Copilot 不让注册了:从“随便用”到“全面限”,agent 把原有订价模型顶穿了 当互联网用AI卷效率时,这家公司先问了一连串“能不能” Meta 开始记录员工每一次点击:AI 要接管工作,先监控会工作的人 Meta“Token榜”逼疯打工人,一夜烧掉公司几万刀!AI时代Token焦虑越来越离谱 智源FlagOS完成DeepSeek-V4-Flash在八款芯片Day0适配,实现三重技术突破 DeepSeek V4 重磅开源!首次打通华为Ascend,也没丢掉英伟达,百万上下文夺回国产模型话语权 李志飞的“新实验”:当超级个体撞上真实组织 GPT-5.5 登顶时刻,Anthropic 亲口承认 Claude 变笨了!网友群嘲:太敷衍 那些没空写的小需求,龙虾真能做吗?_AI&大模型_InfoQ 中文站_InfoQ精选视频 从 Pandas 到生产:使用任意 IDE 进行可扩展的 ML 数据管道与分布式处理 | BUILD 2025_AI&大模型_王玮_InfoQ精选视频 pnpm 11 候选版本发布,带来 ESM 分发、供应链默认设置以及新的存储格式 银行业PDF表格提取方案重构:基于Java的分层方案 GPT-5.5 赢了 Opus 4.7 和 Mythos?奥特曼晒黄仁勋内部信:英伟达全员用上 Codex! Cloudflare 推出 Think:一款面向 AI 代理的持久化运行时 1850亿美元天价支出、75%代码由AI生成!谷歌正式宣告:全面转向智能体工作流 xAI落后太多,马斯克“开大”重金求购Cursor,100亿美金“分手费”都敢签! Pulumi 新增对 Bun 运行时的全面支持 姚顺雨腾讯模型首秀!不卷参数只做 “听话打工人”,Hy3 preview登场 | 附实测 老板让你“忽悠”投资人,你敢发给龙虾吗?_AI&大模型_InfoQ 中文站_InfoQ精选视频 Gemini CLI 引入子代理机制,实现任务委派与并行代理工作流 清华系团队星工聚将完成数千万天使轮融资,轮式机器人拿下头部制造企业亿级大单 Pretext.js 绕过 DOM 布局重排,实现 120 FPS 的高级交互体验 靠“AI 云”爆红的 Vercel,栽在一个第三方AI工具手里!IPO前夕遭黑,200万美元赎金谈崩? 高能研讨会|端侧 AI 正在重写实时感知效率上限_AI&大模型_王玮_InfoQ精选视频 2050大会看这篇就够了|报名、交通食宿指引大全 Java 近期资讯:OpenJDK JEP、Jakarta EE 12、Spring Framework、Micrometer、Camel、JBang 金融智能的架构编排:基于 Snowflake Cortex Agents 实现结构化与非结构化数据统一分析 | 技术实践 在AK大神爆火的任务里,摸清国产AI真实水平 百灵Ling-2.6-flash 正式发布:高 Token 效率,以 1/10 消耗实现 SOTA 级 Agent 能力 当 PM 懂AI,当技术懂产品:AI 时代产品力的双向进化|PM x AI产品力领航者大会即将开幕 为 AI 智能体设计记忆机制:揭秘 LinkedIn 的认知记忆智能体 获奖名单公布|2026主题征文第一期|分享你最有价值的龙虾场景与核心 Skill_热门活动_InfoQ写作社区官方_InfoQ写作社区
时序存储:影响成本与性能的设计选择
作者:Nirmesh K · 2026-05-19 · via InfoQ - 促进软件开发领域知识与创新的传播

使用任何时序数据库都需要做出一系列的存储设计决策:如何布局行、何时进行压缩、基于什么进行分区。这些决策对成本和查询性能的影响,甚至比数据库本身的选择更为关键。本文将从第一性原理出发,系统地探讨这些基本问题,并借助 PostgreSQL 和 Apache Parquet 等广泛可用的工具来量化评估每一项权衡。

什么是时序数据?

时序数据是一系列随时间推移记录的测量值。标准数据库记录追踪的是当前状态(如“账户余额为 50 美元”),而时序数据则追踪变化历程(如“10:00、10:01、10:02 时的余额……”)。

现代系统会持续发出有关运行状况和活动状态的信号:

  • 生物识别技术(智能手表每五秒记录一次心率,以便追踪运动强度)。

  • 交通运输(拼车应用通过追踪 GPS 坐标来计算车费和预计到达时间)。

  • 金融(市场行情显示系统每秒捕捉数千次价格更新)。

  • 基础设施(监控工具追踪 CPU 趋势,以便预测资源耗尽情况)。

廉价的传感器和存储设备使得几乎所有数据都能被即时记录并保存完整的历史数据,而不仅仅是快照。核心数据点遵循一种重复的结构:时间戳、标识符和数值。这种简单性具有欺骗性,因为在海量数据规模下,这种重复性反而会成为问题。以下是一个数据序列示例:

表 1:维度和度量示例

此处,“序列”被定义为一个唯一的标识属性组合。例如,{device=thermostat, location=living_room}。

图 1:多序列和数据点示例

每个数据点包含三个部分:

  • 时间戳——测量发生的时间。

  • 维度(或标签)——用于识别和分组数据序列的属性。

  • 指标(或字段)——该时间点的测量值。

一个实用的原则是:将稳定的标识符放在维度中,将变化的测量值放在指标中。

这种划分很重要,因为它们在查询中的用途不同:维度用于过滤和分组(WHERE、GROUP BY device_id);指标用于计算(AVG(temperature_c)、MAX(humidity_pct))。

关系型存储:扁平化 vs. 规范化

要在 PostgreSQL 这样的关系型数据库中存储时序数据,我们可以将所有属性存储在一个扁平表中,也可以将标识符规范化到一个单独的注册表中。

选项 A :扁平化(简单)模式

扁平模式将维度和指标存储在同一行中。这种布局虽然易于实现和查询,但会导致高度冗余。

CREATE TABLE readings_flat (    ts timestamptz NOT NULL,    device_id text NOT NULL,    location text NOT NULL,   region text NOT NULL,    metric_name text NOT NULL,    value double precision NOT NULL ); CREATE INDEX idx_flat_device_ts ON readings_flat (device_id, ts); 

复制代码

选项 B:规范化模式

规范化操作会将稳定的标识符移入 series_dim 表中。每个测量值都会引用一个 series_id,而不是重复使用标识符字符串。

CREATE TABLE series_dim (series_id serial PRIMARY KEY,device_id text NOT NULL,location text NOT NULL,region text NOT NULL,UNIQUE (device_id, location, region));CREATE TABLE readings_normalized (series_id integer REFERENCES series_dim(series_id),ts timestamptz NOT NULL,metric_name text NOT NULL,value double precision NOT NULL);CREATE INDEX idx_norm_series_ts ON readings_normalized (series_id, ts);

复制代码

存储开销实验结果 

我们通过一项涉及一千个序列和 280 万行数据的 PostgreSQL 16 实验,测量了重复操作的成本。规范化处理使总存储空间减少了约 42%(节省了 289 MB)。

表 2:扁平化模式和规范化模式的存储开销对比

成本模型

效率差距取决于维度字节数与什么相乘。对于扁平化数据,TotalBytes ≈ N_rows * (timestamp + metric + dimensions)。对于规范化数据,TotalBytes ≈ N_rows * (timestamp + metric + series_id) + N_series * dimensions。

这两种模式的总存储量均为 O(N_rows)。区别在于每行的开销:在扁平化模式中,每行都携带完整的维度字符串。而在规范化模式中,每行仅携带一个空间占用较小的序列 ID,而维度字符串则每个序列存储一次。由于序列 ID 远小于完整的维度数据,而且在高频监控中 N_series ≪ N_rows,所以在行数相同的情况下,规范化模式存储的数据量要少得多。

查询性能(缓存预热)

通过对比“扁平化”和“规范化”这两种方式,我们可以获得一些有趣的发现。在范围查询中,两者的性能表现相同:0.74 毫秒(扁平化)vs. 0.74 毫秒(规范化)。而在小时平均值的查询中,规范化方式更快:215.51 毫秒(扁平化)vs. 164.41 毫秒(规范化)。通过仅存储一次维度并使用 ID 进行引用,可以在不增加查询开销的情况下减少存储占用。

高基数时规范化失效

当许多行具有相同的标识时,规范化会有所帮助。但当标识字段具有高基数且每行几乎唯一时,规范化的优势就会减弱。

情况 A:可重复维度(规范化有帮助)

表 3:维度可重复示例

这里,N_rows = 3 且 N_series = 1。规范化布局将维度信息仅在 series_dim 中存储一次,并在每个数据点中复用 series_id。

 情况 B:事件唯一标识符(规范化效果减弱)

表 4:高基数维度示例

如果 request_id 是序列标识的一部分,N_series 将趋近于 N_rows。此时,规范化存储的去重效果将大大降低。从实际应用的角度来看,应将稳定的维度保留在序列标识中。此外,除非查询模式有此要求,否则应将 request_id 等事件级 ID 排除在序列标识之外。

外部的一些指南也体现了同样的基数行为。AWS CloudWatch 将每个唯一的维度组合定义为一个独立的指标流。高基数维度会直接增加指标体量。CloudWatch Logs 的指标过滤器则更进一步:它们明确警告用户不要使用 requestID 和 IPAddress 之类的维度,并且尽可能禁用会产生过多独立流的过滤器。InfluxDB 的基数指南也指出了同样的模式:标签中的唯一 ID、哈希值和随机值会导致序列数量膨胀,并同时降低写入和查询性能。

模式演进设计

固定列的模式在标签演进之前都能正常工作。新标签的引入会导致表结构发生变化,需要进行数据回填以及索引更新。对于时序系统,一种实用的模式是维护一个以 series_id 为键的 series_dim 注册表,并将序列属性存储在 jsonb 类型的 dimensions 字段中,如下所示:

CREATE TABLE series_dim (    series_id bigserial PRIMARY KEY,    dimensions jsonb NOT NULL ); 

复制代码

这种方法在标签发生变化时仍然能保持数据摄入的稳定性。这里的权衡在于,索引构建变成了策略决策,而非一次性 DDL 任务。但索引设计仍然需要考虑。针对维度的通用倒排索引(GIN)对于一般的包含关系和键的存在性过滤非常有用。InfluxDB 采用了类似的倒排索引理念,最初出现在 InfluxDB v1 的元数据索引中,随后在时序索引(TSI)中得到进一步发展。Prometheus 采用了类似的 TSDB 索引模式,在分块读取前将标签匹配器映射到序列 ID(参见 Prometheus 概念Prometheus 存储以及 GitHub 上的 Prometheus 项目)。此外,对于频繁查询的一小部分热门属性,B-树表达式索引非常有用(如 dimensions->>‘region’)。

即使采用这种布局,随着时间的推移,仍然会出现一些反复出现的问题:

  • 标签的增长会导致新键的出现,但其中只有部分键对过滤操作有意义。

  • 索引膨胀会导致索引的 JSON 属性过多,增加写入开销和存储空间。

  • 类型漂移会导致同一个键以不同的类型出现(如“42”与 42),除非进行验证。

  • 基数错误会导致易变键被误认为标识键,从而虚增序列数量。

这种设计可以避免频繁的模式迁移,并保持常见查询路径的高速运行。

列式存储:下一代引擎

在序列标识、基数和模式演进得到控制之后,下一个关键点就是存储布局。列式存储将每一列集中存放。对于时序工作负载,即使采用比较扁平的逻辑模型,这种方法也能获得规范化带来的部分存储优势,因为重复维度的压缩效果良好,而且查询扫描的字节数更少。请看以下 Parquet 模式示例:

message readings {     required int64 ts_utc_ms (TIMESTAMP(MILLIS,true));     required binary series_id (UTF8);    required binary metric_name (UTF8);     required double value;     optional group dimensions (MAP) {         repeated group key_value {             required binary key (UTF8);             optional binary value (UTF8);         }    }} 

复制代码

列式存储选项和查询引擎

上文所述的 Parquet 模式定义了数据在磁盘上的布局方式,但 Parquet 是一种文件格式,而非数据库。选择列式存储也意味着需要决定这些文件的存储位置以及由什么来读取它们。

对象存储上的 Parquet

最简单的部署方式是将 Parquet 文件存储在 Amazon Simple Storage Service (S3) 或与 S3 兼容的存储中,如 Amazon S3 Tables、MinIO 或 GCS。这种方法实现了存储成本与计算成本的分离;你只需按通用价格支付数据保留费用,并在进行查询时支付计算费用。

有多种引擎可以直接从 S3 查询 Parquet 数据。DuckDB 是一款嵌入式分析型数据库,非常适合单节点分析和 CLI 探索。AWS Athena 提供直接从 S3 执行的按查询付费的无服务器 SQL 服务。Trino 和 Presto 是专为大规模交互式查询而设计的联合 SQL 引擎,而 Apache Spark 则是处理大型 Parquet 数据集的标准分布式批处理选项。ClickHouse 可以通过外部表函数读取 Parquet,而 Polars 和 Pandas(通过 PyArrow)等 DataFrame 库原生支持 Parquet,可以很方便地在笔记本中进行即席分析。

请注意,本文中用于说明关系型数据库权衡的 PostgreSQL 数据未提供 Parquet 原生查询功能。一个典型的架构是将数据分层:PostgreSQL 处理“热数据”(低延迟写入和索引查找),而存储在 S3 上的 Parquet 则负责处理“冷数据”(以扫描为主的分析查询)。

使用开放式表格式 Apache Iceberg

虽然将原始 Parquet 文件存储在 S3 上行之有效,但在大规模场景下,你无法获得模式演进、ACID(原子性、一致性、隔离性和持久性)事务以及自动分区管理等功能。Apache Iceberg 在 Parquet 文件之上添加了一层元数据,在将 Parquet 作为物理存储的情况下解决了这些问题。对于时序数据,Iceberg 是一个极佳的默认选项,因为它支持不需要重写数据的模式演进、基于时间的隐式分区、快照隔离以及内置的压缩跟踪。实际上,它最大的优势在于广泛的引擎支持。同一张 Iceberg 表既可由 Spark 或 Flink 写入,也可由 Athena、Trino、DuckDB、ClickHouse、Snowflake、BigQuery、Dremio、StarRocks 或 Polars 查询,而且无需复制或转换数据。S3 Tables 提供了一个托管的 Iceberg 兼容存储,可以自动处理压缩、快照过期和目录集成。

大规模生成 Parquet 数据

对于中等规模的数据量,定期导出(例如一个每晚运行的任务,通过 PyArrow 或 DuckDB 查询 PostgreSQL 并写入 Parquet 格式)已经足够。当数据摄入速率较高时,标准的做法是直接将数据流式传输到 Iceberg;Spark Structured Streaming 和 Apache Flink 都原生支持 Iceberg 接收端,能够处理分区、验证模式以及自动调整文件大小。

Parquet 的压缩优势基于文件大小适中这个前提。实际上,在 S3 上采用列式存储后,文件大小问题是最常见的运维难题之一。如果数据管道每隔几秒钟就刷新一个新的 Parquet 文件,结果就会产生成千上万个小微文件。每个文件都携带自己的尾部元数据,而每次 S3 GET 请求都会产生相应的成本。与打开十个各为 1 MB 的文件相比,打开一万个 1 KB 文件查询速度要慢得多,成本也高得多。针对基于 S3 的分析,一个常见的建议是将每个文件的大小限制在 128 MB 到 1 GB 之间,这样可以在并行处理能力与元数据开销及请求成本之间取得平衡。

对比:规范化 Postgres 与列式 Parquet 

为了测算我们这个 280 万行数据集的存储需求,我们使用最高效的规范化 PostgreSQL 布局,与扁平化的 Parquet 文件进行了对比。

表 5:规范化 PostgreSQL 与 列式 Parquet 存储

基准差距几乎完全取决于压缩效果。Parquet 对包括时间戳在内的每一列都应用字典编码,因此,跨序列重复出现的维度字符串、指标名称,甚至时间戳,都会被压缩为带有整数引用的小型查找表。在本次实验中,1000 个序列和 280 万行数据意味着每一列上都存在大量的重复,这就是为什么它仅靠字典编码就能将文件大小压缩至 1 MB 以下。随着时间序列数量增加、时间分辨率提高以及时间范围延长,时间戳的基数不断增大,字典编码在该列上的效果逐渐减弱,压缩比随之缩小。当添加 UUID 列后,由于每个值都是唯一的,字典编码便完全无法发挥作用。此时文件大小跃升至 100 MB,压缩比从约 434 倍降至约 3.7 倍,但这仍然是一个很有意义的压缩效果。

除了压缩比之外,列式布局在处理时间序列工作负载方面还具有更广泛的优势:

优化存储空间占用

由于列式存储将多行数据的值集中存储在一起,所以系统能够根据各列的数据类型和模式,单独针对每一列应用最高效的压缩算法,例如增量(Delta)、RLE 或字典编码。这种专门的压缩技术可以合并连续的时间戳并去除稳定维度的重复数据,与未压缩的行式存储相比,数据量可减少数个数量级。

高效分析读取

查询仅扫描所需的列段,可以避免 I/O 放大效应。这种选择性投影有助于针对特定的指标快速进行聚合,而无需承担读取整行数据的开销。

存储更便宜

将 Parquet 文件存储在通用对象存储(例如 S3)中,既能实现经济高效的长期保留,又能将归档历史数据与活跃数据库所需的高性能 SSD 分离。

如需进一步阅读,请参阅:列式存储与行式存储:它们究竟有多大区别?(SIGMOD 2008)

多指标行的宽模式与窄模式

当多个指标在同一时间戳发布时,行结构会同时影响存储开销和查询复杂度。窄(长格式)模式为每个指标存储一行:

表 6:窄模式:对于每个时间戳,一个指标一行,序列标识与时间戳在每行中重复出现

宽模式一行存储多个指标列:

表 7:宽模式:每行对应一个时间戳,所有指标均作为独立的列存储

权衡:

  • 窄模式:每行指标数据都包含 (series_id, ts)。

  • 宽模式: (series_id, ts) 仅针对该时间戳存储一次。

  • 如果需要查询对应同一时间戳的多个指标,窄模式通常需要在 (series_id, ts) 上进行转置或自连接。

  • 宽模式通过读取单行即可返回这些指标,从而使查询逻辑更简单。

随着每个时间戳对应的指标数增加,窄模式会将更多的存储空间用于存储重复的键值,而非新信息。这种开销既体现在数据中,也体现在索引中。只要指标保持稳定且同时发布,宽模式就能避免大部分重复。

窄模式自连接示例:

SELECT t.series_id, t.ts, t.value AS temperature_c, h.value AS humidity_pct FROM readings_narrow t JOIN readings_narrow h    ON h.series_id = t.series_id    AND h.ts = t.ts WHERE t.metric_name = 'temperature_c'    AND h.metric_name = 'humidity_pct'    AND t.series_id = 'A1'    AND t.ts >= now() - interval '1 hour';

复制代码

宽模式查询:

SELECT series_id, ts, temperature_c, humidity_pct FROM readings_wide WHERE series_id = 'A1' AND ts >= now() - interval '1 hour';

复制代码

实用规则:

  • 当指标集规模较小、稳定且同时生成时,应使用宽模式。

  • 当常见查询需要在同一结果行中包含同一时间戳的多个指标时,应优先选择宽模式。

  • 当指标稀疏、动态变化或独立采样时,应使用窄模式。

  • 当指标集快速演进时,宽模式可能会积累大量可为空的列。

分区:先按时间,再按空间

到目前为止,我们一直假设数据存储在单个表中。随着数据量的增长,单表结构越来越难以高效地查询,维护成本也随之增加。分区技术将数据集拆分为多个相对较小且可独立管理的块。在单数据库实例上,这种方法可以优化查询剪枝、简化数据保留策略,并降低维护开销。如果单个实例不再满足需求,这些分区边界随后将成为将数据分布到不同节点的自然单元。

时间分区

时间是天然的分区轴。时序查询几乎总是有时间范围限制(例如“过去一小时”或“过去 7 天”),而且数据本身的内置排序与数据到达顺序一致。

将数据按时间范围拆分成块可带来以下三点好处:

分区修剪

针对过去一小时的查询仅涉及当前分区。规划器仅通过元数据即可排除不匹配的分区(无需进行索引扫描,也无需进行 I/O 操作)。

数据过期清理成本低

删除旧分区是一项 O(1) 级的元数据操作。而从单个表中删除行则需要进行扫描、标记和 vacuum 操作。对于大型表而言,这些操作可能需要好几分钟的时间,并且会导致 I/O 突发。

独立维护

每个分区都可以独立地进行 vacuum、重新索引或分析。活跃(当前)分区会频繁进行维护;冷分区则保持不变。每个分区的索引也更小、更浅,因此查找速度更快,而且内存使用情况可预测。

在 PostgreSQL 中,这种方法是声明式的:

CREATE TABLE readings (    series_id integer NOT NULL,    ts timestamptz NOT NULL,    metric_name text NOT NULL,    value double precision NOT NULL ) PARTITION BY RANGE (ts); CREATE TABLE readings_2026_02_01    PARTITION OF readings    FOR VALUES FROM ('2026-02-01') TO ('2026-02-02'); CREATE TABLE readings_2026_02_02    PARTITION OF readings    FOR VALUES FROM ('2026-02-02') TO ('2026-02-03');

复制代码

包含 WHERE ts BETWEEN ... 子句的查询会自动仅匹配相关分区。可以通过执行 DROP TABLE readings_2026_01_15 来删除旧数据,而无需进行行级清理。

为什么单用时间分区会导致热点 

时序写入具有一个特性:它们集中在“当下”这一时间点。每个活跃的时序都会将其最新数据点写入同一个当前时间窗口内。如果仅按时间进行分区,当前分区将吸收所有写入流量,而历史分区则处于闲置状态。

对于一个每秒处理 10 万个数据点、涵盖 1 万个时间序列的系统,当前每日分区每秒需要处理全部 10 万次写入操作。前一天的分区则完全没有写入。这种设计本身就形成了写入热点。读取操作也会出现同样的问题。比如,“给我 device-42 过去一小时的数据”这样的查询会命中当前分区,但该分区包含这一小时内所有序列的数据。如果没有进一步的数据组织机制,该查询将扫描所有 1 万个序列的数据来查找所需的那个序列。

二维分区:时间和空间

解决方法是按两个维度进行分区:时间和序列标识。生成的的每个分区都覆盖一个时间范围和一个序列子集。采用这种方法,即使在当前时间窗口内,也能将写入操作分散到多个分区。

通常,“空间”维度是由序列标识衍生而来的:例如序列标识(series_id)的哈希值,或是基于区域或设备类型等的自然分组方式。

图 2:二维时序分区可视化

现在,当日每秒 10 万次的写入操作分布在三个分区上,而不是一个分区。针对特定序列的查询只会访问包含其哈希桶且时间范围相符的分区。如果想深入了解这种二维分区在大型托管系统中的运作原理,请参阅 Werner Vogels 关于 Amazon Timestream 设计的文章

降采样与保留:以分辨率换成本

全分辨率原始时序数据的有效期很短。在事件发生时,每五秒一次的 CPU 读数都至关重要。一周后,分钟级数据变化趋势就已经足够。一年后,人们查询的只有每小时的平均值。

降采样是随着时间的推移降低数据分辨率,方法是预先计算更粗时间间隔的聚合数据。保留策略则规定了何时彻底丢弃数据。二者共同控制着存储成本的增长曲线。

分辨率阶梯

下图是一种典型的策略:

图 3:分辨率阶梯

每一步都会按时间间隔的比例减少行数。从 5 秒分辨率降至 1 分钟分辨率,行数减少 12 倍;从 1 分钟分辨率降至 1 小时分辨率,行数再减少 60 倍。一个每天产生 17280 个原始数据点的序列,在 1 分钟分辨率下变为 1440 个,在 1 小时分辨率下变为 24 个,减少了 720 倍。

仪表板刷新的成本挑战

在存储和保留成本得到控制的情况下,剩下的成本驱动因素就是读取放大效应,而仪表盘是其最常见的来源。

单个仪表盘的查询成本可能很低。但在大规模环境下,刷新流量会迅速推高成本。如果:

U = 并发用户数 Q = 每个仪表板试图的查询数R = 刷新间隔(秒)

复制代码

那么:

QPS ~= (U * Q) / R 

复制代码

数据摄入速率可能很平稳,但读取成本仍然可能激增,因为系统会反复重新计算相同时段的聚合结果。

实际中可考虑的缓解措施:

  • 使用短时效(TTL)缓存结果。

  • 通过预先聚合的汇总数据而非原始行数据来响应仪表盘查询。

  • 在条件允许的情况下,利用仪表盘/查询层中的查询缓存功能(如 Grafana 搭配 Amazon Timestream 使用)。

结论

时序数据库底层的存储决策(包括如何对标识符进行建模、基于何种维度进行分区、何时进行降采样以及如何布局列)对成本和性能的影响远大于数据库选择本身。无论你运行的是 PostgreSQL、S3 上的 Parquet,还是托管的时序服务,这些基本原则都适用。只要能正确地处理这些方面,无论选择哪种数据库,你都能避免那些表现为意外账单和仪表盘运行缓慢的问题。

原文链接:https://www.infoq.com/articles/time-series-storage-design/