一篇读懂时序数据库:Prometheus 与 TSDB 的存储结构

监控数据的「性格」

监控/指标数据的访问模式非常独特:写入源源不断(每个指标每秒都在上报——写入远大于更新(几乎不更新(历史数据不修改——只追加);按时间+标签查询(「查最近一小时的 CPU 使用率」——时间范围+标签过滤;有保留期(老数据过期删除(不删业务数据那样宝贵);聚合分析为主(按时间聚合(sum/avg/rate——不查单条。

这些特征与 OLTP 数据库(行存储、事务、更新)完全不匹配——强行用 MySQL 存监控(有人这么做过——大表的写入瓶颈、索引膨胀、过期删除的灾难——催生了时序数据库(TSDB, Time-Series Database)**的专门设计。这篇以 Prometheus 的 TSDB 为例,拆解 TSDB 的存储结构。

TSDB 的核心设计:针对「时序」的极致优化

设计一:时间分片(time partitioning)。数据按时间分块存储(「块(block)」:Prometheus 的 TSDB 把时间切成 2 小时的块(block)——每个块是一个不可变目录(chunks 的数据文件 + 索引 + 元数据——「不可变」的好处:块写入完成后不再修改(无锁、可压缩、缓存友好——「块的目录结构」:block 目录(chunks/、index、meta.json——「不可变块」是 TSDB 与通用数据库的根本区别。「内存块与磁盘块」:当前的 2 小时数据在内存(head block——内存块持久化 WAL(宕机恢复——「head 的写路径」:样本进内存索引与 chunk 内存——「head compaction」:内存块满 2 小时 → 持久化为磁盘块——「写入只加内存,磁盘只加块」——没有随机写。

设计二:列存与压缩。时序数据的列存优势(同列的数据类型一致(时间戳都是 int64、值同类型——「列存的压缩率」:相邻时间戳的差值很小(15 秒间隔的差值恒定——delta-of-delta 编码(差值的差值——大多为 0——「1 bit 表示恒定间隔」的压缩——Gorilla 编码(Facebook 的时序压缩论文(「Gorilla 的压缩率」(12 倍左右——值列的压缩(XOR 编码(相邻值的位差异——「列存+差分编码」让时序数据的存储成本极低——这是 TSDB 相对通用数据库的成本优势。

设计三:倒排索引与标签。标签(labels)的索引——「标签的倒排索引」(每个标签值 → 包含它的时间序列列表(「series 的定义」(指标名+标签集的唯一组合=一条时间序列——「序列爆炸」的问题(标签值的组合爆炸(每个用户一个标签=百万序列——「基数(cardinality)」(基数是 TSDB 的第一大运维难题——高基数(unbounded labels——把用户 ID、请求 ID 放标签——序列爆炸——内存与索引的灾难——「基数的治理」:标签的设计纪律(有限集合的值做标签、无限集合(ID)放日志不做标签——「标签设计」是 TSDB 使用的第一课。

graph TD
    A[TSDB 三大设计] --> B[时间分块: 2 小时不可变块<br/>head 内存块 + WAL]
    A --> C[列存压缩: delta-of-delta + XOR<br/>Gorilla 压缩率 ~12x]
    A --> D[倒排索引: 标签 → 序列<br/>基数治理是第一难题]

Prometheus 的架构与查询

Prometheus 的整体架构:Pull 模型(Prometheus 拉取目标的指标——「Pull vs Push」(Pull 的优点(目标自描述、失败检测(拉不到=目标挂了)、Push 的场景(短生命周期任务(Pushgateway——「Pull 模型」的服务发现( Prometheus 的服务发现(Kubernetes 的自动发现——「自动发现」让监控的目标管理自动化。PromQL 的查询语言:时序查询的表达力(「PromQL 的核心概念」:即时向量(某一时刻的每个序列的值)、范围向量(时间范围内的序列——「rate 的计算」(rate(http_requests_total[5m])——范围向量的增长率——「PromQL 的聚合」(sum by (service)——按标签聚合——「PromQL 的表达力」是 Prometheus 的核心竞争力(查询即 API。Prometheus 的局限:单机的扩展性(「单实例的极限」(内存与查询的瓶颈(「联邦(federation)」(多 Prometheus 的分层聚合——「远程存储」(长期存储外挂(Thanos、VictoriaMetrics、Mimir——「Prometheus 生态」的扩展路径——单机抓取+分布式存储的组合。

降采样与长期存储:「降采样(downsampling)」:老数据的粒度降低(原始 15 秒 → 5 分钟聚合——「降采样」的空间收益(存储量随时间衰减——「保留策略」(原始数据 30 天、降采样 1 年、再降采样 5 年——「多级保留」的存储成本结构——「TSDB 的成本模型」:写入量×压缩率×保留期——「保留策略」是成本的调节旋钮。

TSDB 的进阶:基数治理的实操与「可观测的成本」

TSDB 的进阶:基数治理的实操方案与可观测性的成本治理。「基数治理」的实操:「基数问题」的诊断:TSDB 的基数指标(Prometheus 的 head series——「series 数的监控」——「爆炸的时间点」定位(新版本上线引入高基数标签——「label 的审计」(找出哪些标签是高基数——「高基数的元凶」(user_id、trace_id、url 的高基数——「诊断的 PromQL」(topk 查询序列数最多的指标。「基数的治理手段」:标签的删减(「不必要标签」的移除——「标签的值域约束」(枚举值做标签、ID 做日志——「cardinality limit」(Prometheus 的 label_limits——「metric_relabel_configs」(采集后的标签处理(丢弃高基数标签——「relabel」的强制。「TSDB 的降采样与长期存储」:Thanos 的架构(Sidecar+Query+Store——「Thanos 的组件」(Sidecar 上传块、Compactor 降采样——「降采样的分辨率」(5 分钟/1 小时——「长期存储的对象化」(对象存储的成本优势——「VictoriaMetrics 的对比」(VM 的单机性能与简单性——「VM vs Thanos」的选型(「VM 的优势」(单机高性能、简单——「Thanos 的优势」(与 Prometheus 原生集成——「Mimir」(Grafana 的企业版——「三个方案的对比表」。「可观测的成本治理」:「可观测的成本构成」:采集的量(指标的样本数——「样本的成本」(每个样本的存储与查询——「指标数量」的治理(「无用的指标」清理——「采集间隔」的调优(30s vs 15s 的成本差——「trace 的采样率」(trace 的全量 vs 采样——「采样的策略」(头部采样 vs 尾部采样(「尾部采样」(按错误与延迟采——「尾部采样」的精细——「日志的采样」(debug 日志的量——「可观测的成本监控」(可观测性自身的成本监控——「可观测性花多少钱」——「占 IT 成本的占比」——「成本大盘」的可观测。「成本与价值的对齐」:「每个指标的价值」(这个指标谁在用、谁在告警——「指标的使用审计」——「无主的指标删除」——「可观测性的 ROI」——故障发现的速度 vs 成本——「ROI」的周期评估。TSDB 的进阶总结:基数治理的诊断与手段(标签审计、relabel、值域约束)+长期存储的方案对比(Thanos/VM/Mimir)+可观测的成本治理(采样、保留、指标审计)——「可观测性是花钱的艺术」——它不直接产生收入,但省下的每一次故障都是利润——「成本的显式化」与「基数的纪律」,是可观测性从「烧钱」到「投资」的两步转变。

TSDB 高频问答

收尾前回答三个高频问题。「Prometheus 挂了监控就瞎了怎么办?」——监控的冗余体系:「Prometheus 的高可用」:双副本(「双 Prometheus」(相同抓取、Alertmanager 去重——「双副本的查询」(双副本的查询负载——「分片」的方案(按 job 分片——「监控的高可用」与「监控自身的监控」(「监控监控」的元监控——「meta-monitoring」的实践——「监控瞎了」的场景防御——「第二个监控系统」的兜底(「不同技术栈」的第二套(「异构兜底」的理念。「指标太多存不下怎么办?」——多级手段:「存储成本的治理」:降采样(「5 分钟聚合」的长期存储——保留期分级(「15s 保留 30 天、5m 保留 1 年、1h 保留 3 年」——「多级保留」的成本结构——「指标的分值」(重要的多留、无用的删——「指标审计」(「谁在用」的审计——「无主指标」的清理——「采集间隔」的调优(「30s → 60s」的成本减半——「采集粒度」与「告警灵敏度」的权衡。「TSDB 用 MySQL 行不行?」——不行或勉强:「MySQL 存时序的问题」:写入瓶颈(每秒百万样本的写入——「索引的膨胀」(时间+标签的复合索引——「过期删除」的灾难(DELETE 的代价——「分区的删除」(DROP PARTITION 的缓解——「MySQL 存时序的勉强方案」(分区表+定期 DROP——「勉强方案的极限」——「TSDB 的选型」是监控体系的第一次决策——「选对了后面都顺,选错了处处是坑」。TSDB 的问答收束:TSDB 的运维三大件——「基数的纪律(标签设计)、成本的治理(降采样与保留分级)、监控的冗余(双副本+异构兜底)」——「TSDB 挂了=监控瞎了=故障盲飞」——TSDB 的可用性要求,比业务系统还要高——「监控自身的可靠性」是可观测性的元问题。

小结

时序数据库的设计与生态:三大设计(时间分块的不可变块——无随机写、列存的差分压缩——Gorilla 的 12 倍压缩率、倒排索引与标签——基数治理是第一难题);Prometheus 的架构(Pull 模型与服务发现、PromQL 的表达力、单机极限与 Thanos 类的分布式扩展、降采样与多级保留策略)。给使用者的两个判断:「监控类数据用 TSDB,不要用通用数据库硬扛」(访问模式不匹配的代价是写入瓶颈与运维灾难);「标签设计是 TSDB 使用的第一课」(基数治理决定 TSDB 的生死——把 ID 类的高基数值放进标签,是监控事故的常见起点)。TSDB 的设计哲学一句话:为一种「性格极其鲜明」的数据(只写不改、按时间查、有保留期)做极致的存储设计——通用性换效率——这是所有专用存储(时序、向量、图)的共同哲学,也是「数据库选型」这门学问的核心逻辑。

监控数据的天生性格,决定了它需要一个天生为它设计的家——TSDB 就是那个家。

← 返回资讯列表

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?