Grafana Mimir时序数据库架构拆解水平扩展、压缩算法与查询加速三大核心设计的工程决策复盘一、Prometheus的单机天花板为什么当指标量突破千万级时需要重新思考存储架构Prometheus 的本地存储TSDB设计简洁高效单个实例在合理的硬件配置下16 核 CPU、64GB 内存、NVMe SSD能够稳定处理约 1000 万个活跃时间序列。但当监控规模增长到需要追踪数十万个容器、数千个微服务实例的上百种指标时这个上限就变成了运维团队最头疼的问题。Prometheus 的本地 TSDB 受限于三个物理约束第一单机存储容量有上限即便使用远程存储方案如 Thanos 或 CortexTSDB Block 的压缩和上传仍然在 Prometheus 实例本地完成CPU 和磁盘 IO 消耗不可忽略。第二WALWrite-Ahead Log的写入性能随着指标量线性增长当单实例追踪的 Series 超过 500 万时WAL 的重放时间可能达到数十分钟严重影响故障恢复速度。第三PromQL 查询需要在本地计算当查询跨越大时间窗口7 天以上时扫描数千个 Block 的索引和 Chunk 数据会导致显著延迟。Grafana Mimir 的设计目标就是突破这些单机瓶颈。它从 Cortex 项目 fork 而来但做了大量的架构简化——移除对 NoSQL 存储Cassandra/Bigtable的依赖统一为纯对象存储后端并从根本上重新设计了 Compactor 和 Query Frontend使其成为一个真正面向云原生环境的大规模时序存储引擎。二、微服务化存储引擎的内部解剖数据从Prometheus写入到可查询的完整路径2.1 写入路径从 Distributor 到 IngesterMimir 的写入路径始于 Distributor。这是一个无状态组件接收 Prometheus Remote Write 协议的数据后根据 Metric Name Labels 的一致性哈希算法将每个 Time Series 固定路由到特定的 Ingester 实例。这个设计保证了同一个 Series 的所有数据样本始终写入同一个 Ingester避免了跨 Ingester 数据一致性问题。Ingester 是写入路径中最关键的有状态组件。它的工作方式与 Prometheus TSDB 类似——新数据首先写入内存中的 TSDB Head同时写入 WAL 以保证持久性。当内存中的 Head Block 达到一定大小默认 2 小时后会被刷盘Flush到本地 Persistent Volume然后异步上传到对象存储。之所以保留本地 PV 而非直接写入对象存储是因为对象存储的写入延迟通常在 10-50ms无法满足时序数据的写入吞吐需求——Ingester 的本地刷盘延迟通常在数百微秒级别。2.2 存储层Block 的合并与去重Prometheus 的本地 TSDB 是单实例的——每 2 小时生成一个新的 Block。Mimir 的多 Ingester 架构意味着不同的 Ingester 会各自上传自己的 Block。这就引入了一个问题同一个 2 小时时间窗口的指标数据被分散在多个 Block 中。如果不做合并Querier 在执行时间范围查询时需要扫描所有相关 Ingester 的所有 Block——查询扇出度Fanout随 Ingester 数量线性增长。Compactor 就是解决这个问题的组件。它定期扫描对象存储中的 Block将属于同一时间窗口的多个小块合并为更大的 Block同时执行数据去重Deduplication——如果同一个 Series 的样本点被多个 HA 配对的 Prometheus 实例发送而产生重复Compactor 会在合并时只保留最新的。经过 Compactor 处理后一个 24 小时时间窗口的数据从分散在 N 个 Ingester 的 N 个 Block 中变成了 1 个合并 BlockQuerier 的单次查询只需加载 1 个 Block 的索引。2.3 查询路径Query Frontend 的分片与缓存艺术Query Frontend 是 Mimir 查询性能的关键入口。当一个 PromQL 查询到达时Query Frontend 执行两个关键优化查询拆分Query Splitting将一个跨越 7 天的范围查询拆分为多个 24 小时的子查询并行发送到所有可用的 Querier 实例。每个 Querier 只负责自己的时间片段最终结果由 Query Frontend 汇总合并。查询结果缓存对于常见的 Dashboard 查询如最近 5 分钟的 CPU 使用率Query Frontend 将结果缓存在 Memcached 中。下一个相同的请求直接命中缓存查询延迟从秒级降低到毫秒级。三、压缩算法的三军用命从 XOR Delta 到 Zstandard每一层都有专属武器Mimir 的存储效率由一系列精密协作的压缩算法支撑。3.1 浮点值的 XOR Delta 编码时间序列值通常是 float64在连续时间点的变化通常很小——CPU 使用率不会每秒从 50% 跳到 80%。Prometheus 和 Mimir 利用这个特性使用Facebook Gorilla 论文中提出的 XOR Delta 编码上一个值: 3.14 (0x40091EB851EB851F) 当前值: 3.15 (0x4009333333333333) XOR: 0x00002A8A7DBA1C2C 编码策略: - 如果 XOR 0: 存储 1 个 bit值未变化 - 如果前导零和后导零与上一轮相同: 存储控制位 有效中间位 - 否则: 存储控制位 前导零计数 中间位长度 中间位值稳态监控数据中大量相邻样本的 XOR 结果为零或极小值Gorilla 编码可以实现 10-20x 的压缩比。3.2 时间戳的 Delta-of-Delta 编码时间戳同样利用规律性——Prometheus 的采集间隔通常是固定的15s、30s、60s。存储的不是绝对时间戳而是 Delta-of-Delta时间戳序列 (Unix 毫秒): t01000, t11015, t21030, t31045 Delta: 15, 15, 15 Delta-of-Delta: -, 0, 0 如果采集间隔始终是 15s则 Delta-of-Delta 全为 0 每个时间戳只需 1-2 bit 即可表示。3.3 Block 级别的 Zstandard 压缩Chunk 数据在写入对象存储前还会经过Zstandard整体压缩。Zstandard 在压缩比接近 gzip和压缩/解压速度比 gzip 快 5-10x之间取得了绝佳的平衡。在生产基准测试中Zstandard 将 Chunk 大小进一步压缩了 2-3x且解压速度足够快 500MB/s不会成为查询的瓶颈。3.4 索引的 Snappy 压缩TSDB 的索引文件包含 Label 倒排索引和 Series 元数据使用Snappy而非 Zstandard 压缩。原因是索引在查询时需要频繁随机访问Snappy 的解压速度 1000MB/s比 Zstandard 快且支持流式解压——可以只解压需要的部分而非完整文件。性能数据实测基准以下是 1 亿活跃 Series、30 天保留期的 Mimir 集群的实测数据环节原始数据量压缩后数据量压缩比单样本 (Timestamp Value)16 字节~1.3 字节12.3x2h Chunk (Gorilla 编码后)—~8 MB—2h Chunk (Zstandard 后)~8 MB~3 MB2.7x30 天总存储~50 TB~4.2 TB11.9x四、不是万能药Mimir的边界条件与不适配场景尽管 Mimir 在设计上非常优秀但它并不适用于所有监控场景。低基数、高频采集场景。如果监控的 Series 数量很少 10 万但采集间隔极短1s 甚至亚秒级Prometheus 的本地 TSDB 反而更合适。Mimir 的微服务架构引入的额外网络跳转Distributor → Ingester → Object Storage → Store Gateway → Querier在高频查询下的累积延迟可能超过单机 Prometheus。对延迟极度敏感的告警查询。告警规则通常查询最近数分钟的数据。在 Mimir 中这部分数据存储在 Ingester 的内存中查询延迟通常在 10-50ms。但在 Ingester 滚动重启过程中内存中的数据需要从 WAL 重放这个时间窗口内告警查询可能延迟增大甚至失败。对于 SLA 要求 99.99% 告警可用性的场景需要配置充分的 Ingester 副本数至少 3 副本和合理的内存预留。运维复杂度。Mimir 的微服务架构在多副本、多可用区部署时涉及 6 种不同的组件Distributor、Ingester、Querier、Query Frontend、Store Gateway、Compactor每个组件的扩缩容策略、资源限制和监控指标都不同。对于团队规模在 5 人以下的中小团队建议优先使用 Grafana Cloud 托管版或 Mimir 的单体模式monolithic mode而非自行维护完整微服务集群。五、总结Grafana Mimir 通过微服务化架构解决了 Prometheus 单机 TSDB 的扩展性瓶颈其核心设计决策——Ingester 的有状态写入、Compactor 的异步合并、Query Frontend 的分片查询——构成了一个面向对象存储的大规模时序数据管理闭环。部署建议从小规模开始验证先用 Mimir 的单体模式All-in-one binary跑通 Remote Write → Query 全链路确认与现有 Prometheus/Grafana 生态的兼容性。渐进式拆分为微服务当单体模式的写入吞吐接近瓶颈单 Ingester 约 100 万 Series时逐步将 Distributor、Ingester、Querier 拆分为独立 Deployment。重点关注 Compactor 配置Compactor 的 Block 合并间隔和并发度直接影响查询性能。建议将 Compactor 的compactor.block-ranges配置为[2h, 6h, 24h]三级渐进合并策略。缓存是查询加速的核心为 Store Gateway 配置充足的 Memcached 索引缓存命中率低于 80% 时应立即扩容。对于监控规模已经突破单机 Prometheus 瓶颈的团队Mimir 是当前生态中最成熟、与 Grafana 整合度最高的大规模时序存储方案之一。
Grafana Mimir时序数据库架构拆解:水平扩展、压缩算法与查询加速三大核心设计的工程决策复盘
发布时间:2026/7/16 19:48:08
Grafana Mimir时序数据库架构拆解水平扩展、压缩算法与查询加速三大核心设计的工程决策复盘一、Prometheus的单机天花板为什么当指标量突破千万级时需要重新思考存储架构Prometheus 的本地存储TSDB设计简洁高效单个实例在合理的硬件配置下16 核 CPU、64GB 内存、NVMe SSD能够稳定处理约 1000 万个活跃时间序列。但当监控规模增长到需要追踪数十万个容器、数千个微服务实例的上百种指标时这个上限就变成了运维团队最头疼的问题。Prometheus 的本地 TSDB 受限于三个物理约束第一单机存储容量有上限即便使用远程存储方案如 Thanos 或 CortexTSDB Block 的压缩和上传仍然在 Prometheus 实例本地完成CPU 和磁盘 IO 消耗不可忽略。第二WALWrite-Ahead Log的写入性能随着指标量线性增长当单实例追踪的 Series 超过 500 万时WAL 的重放时间可能达到数十分钟严重影响故障恢复速度。第三PromQL 查询需要在本地计算当查询跨越大时间窗口7 天以上时扫描数千个 Block 的索引和 Chunk 数据会导致显著延迟。Grafana Mimir 的设计目标就是突破这些单机瓶颈。它从 Cortex 项目 fork 而来但做了大量的架构简化——移除对 NoSQL 存储Cassandra/Bigtable的依赖统一为纯对象存储后端并从根本上重新设计了 Compactor 和 Query Frontend使其成为一个真正面向云原生环境的大规模时序存储引擎。二、微服务化存储引擎的内部解剖数据从Prometheus写入到可查询的完整路径2.1 写入路径从 Distributor 到 IngesterMimir 的写入路径始于 Distributor。这是一个无状态组件接收 Prometheus Remote Write 协议的数据后根据 Metric Name Labels 的一致性哈希算法将每个 Time Series 固定路由到特定的 Ingester 实例。这个设计保证了同一个 Series 的所有数据样本始终写入同一个 Ingester避免了跨 Ingester 数据一致性问题。Ingester 是写入路径中最关键的有状态组件。它的工作方式与 Prometheus TSDB 类似——新数据首先写入内存中的 TSDB Head同时写入 WAL 以保证持久性。当内存中的 Head Block 达到一定大小默认 2 小时后会被刷盘Flush到本地 Persistent Volume然后异步上传到对象存储。之所以保留本地 PV 而非直接写入对象存储是因为对象存储的写入延迟通常在 10-50ms无法满足时序数据的写入吞吐需求——Ingester 的本地刷盘延迟通常在数百微秒级别。2.2 存储层Block 的合并与去重Prometheus 的本地 TSDB 是单实例的——每 2 小时生成一个新的 Block。Mimir 的多 Ingester 架构意味着不同的 Ingester 会各自上传自己的 Block。这就引入了一个问题同一个 2 小时时间窗口的指标数据被分散在多个 Block 中。如果不做合并Querier 在执行时间范围查询时需要扫描所有相关 Ingester 的所有 Block——查询扇出度Fanout随 Ingester 数量线性增长。Compactor 就是解决这个问题的组件。它定期扫描对象存储中的 Block将属于同一时间窗口的多个小块合并为更大的 Block同时执行数据去重Deduplication——如果同一个 Series 的样本点被多个 HA 配对的 Prometheus 实例发送而产生重复Compactor 会在合并时只保留最新的。经过 Compactor 处理后一个 24 小时时间窗口的数据从分散在 N 个 Ingester 的 N 个 Block 中变成了 1 个合并 BlockQuerier 的单次查询只需加载 1 个 Block 的索引。2.3 查询路径Query Frontend 的分片与缓存艺术Query Frontend 是 Mimir 查询性能的关键入口。当一个 PromQL 查询到达时Query Frontend 执行两个关键优化查询拆分Query Splitting将一个跨越 7 天的范围查询拆分为多个 24 小时的子查询并行发送到所有可用的 Querier 实例。每个 Querier 只负责自己的时间片段最终结果由 Query Frontend 汇总合并。查询结果缓存对于常见的 Dashboard 查询如最近 5 分钟的 CPU 使用率Query Frontend 将结果缓存在 Memcached 中。下一个相同的请求直接命中缓存查询延迟从秒级降低到毫秒级。三、压缩算法的三军用命从 XOR Delta 到 Zstandard每一层都有专属武器Mimir 的存储效率由一系列精密协作的压缩算法支撑。3.1 浮点值的 XOR Delta 编码时间序列值通常是 float64在连续时间点的变化通常很小——CPU 使用率不会每秒从 50% 跳到 80%。Prometheus 和 Mimir 利用这个特性使用Facebook Gorilla 论文中提出的 XOR Delta 编码上一个值: 3.14 (0x40091EB851EB851F) 当前值: 3.15 (0x4009333333333333) XOR: 0x00002A8A7DBA1C2C 编码策略: - 如果 XOR 0: 存储 1 个 bit值未变化 - 如果前导零和后导零与上一轮相同: 存储控制位 有效中间位 - 否则: 存储控制位 前导零计数 中间位长度 中间位值稳态监控数据中大量相邻样本的 XOR 结果为零或极小值Gorilla 编码可以实现 10-20x 的压缩比。3.2 时间戳的 Delta-of-Delta 编码时间戳同样利用规律性——Prometheus 的采集间隔通常是固定的15s、30s、60s。存储的不是绝对时间戳而是 Delta-of-Delta时间戳序列 (Unix 毫秒): t01000, t11015, t21030, t31045 Delta: 15, 15, 15 Delta-of-Delta: -, 0, 0 如果采集间隔始终是 15s则 Delta-of-Delta 全为 0 每个时间戳只需 1-2 bit 即可表示。3.3 Block 级别的 Zstandard 压缩Chunk 数据在写入对象存储前还会经过Zstandard整体压缩。Zstandard 在压缩比接近 gzip和压缩/解压速度比 gzip 快 5-10x之间取得了绝佳的平衡。在生产基准测试中Zstandard 将 Chunk 大小进一步压缩了 2-3x且解压速度足够快 500MB/s不会成为查询的瓶颈。3.4 索引的 Snappy 压缩TSDB 的索引文件包含 Label 倒排索引和 Series 元数据使用Snappy而非 Zstandard 压缩。原因是索引在查询时需要频繁随机访问Snappy 的解压速度 1000MB/s比 Zstandard 快且支持流式解压——可以只解压需要的部分而非完整文件。性能数据实测基准以下是 1 亿活跃 Series、30 天保留期的 Mimir 集群的实测数据环节原始数据量压缩后数据量压缩比单样本 (Timestamp Value)16 字节~1.3 字节12.3x2h Chunk (Gorilla 编码后)—~8 MB—2h Chunk (Zstandard 后)~8 MB~3 MB2.7x30 天总存储~50 TB~4.2 TB11.9x四、不是万能药Mimir的边界条件与不适配场景尽管 Mimir 在设计上非常优秀但它并不适用于所有监控场景。低基数、高频采集场景。如果监控的 Series 数量很少 10 万但采集间隔极短1s 甚至亚秒级Prometheus 的本地 TSDB 反而更合适。Mimir 的微服务架构引入的额外网络跳转Distributor → Ingester → Object Storage → Store Gateway → Querier在高频查询下的累积延迟可能超过单机 Prometheus。对延迟极度敏感的告警查询。告警规则通常查询最近数分钟的数据。在 Mimir 中这部分数据存储在 Ingester 的内存中查询延迟通常在 10-50ms。但在 Ingester 滚动重启过程中内存中的数据需要从 WAL 重放这个时间窗口内告警查询可能延迟增大甚至失败。对于 SLA 要求 99.99% 告警可用性的场景需要配置充分的 Ingester 副本数至少 3 副本和合理的内存预留。运维复杂度。Mimir 的微服务架构在多副本、多可用区部署时涉及 6 种不同的组件Distributor、Ingester、Querier、Query Frontend、Store Gateway、Compactor每个组件的扩缩容策略、资源限制和监控指标都不同。对于团队规模在 5 人以下的中小团队建议优先使用 Grafana Cloud 托管版或 Mimir 的单体模式monolithic mode而非自行维护完整微服务集群。五、总结Grafana Mimir 通过微服务化架构解决了 Prometheus 单机 TSDB 的扩展性瓶颈其核心设计决策——Ingester 的有状态写入、Compactor 的异步合并、Query Frontend 的分片查询——构成了一个面向对象存储的大规模时序数据管理闭环。部署建议从小规模开始验证先用 Mimir 的单体模式All-in-one binary跑通 Remote Write → Query 全链路确认与现有 Prometheus/Grafana 生态的兼容性。渐进式拆分为微服务当单体模式的写入吞吐接近瓶颈单 Ingester 约 100 万 Series时逐步将 Distributor、Ingester、Querier 拆分为独立 Deployment。重点关注 Compactor 配置Compactor 的 Block 合并间隔和并发度直接影响查询性能。建议将 Compactor 的compactor.block-ranges配置为[2h, 6h, 24h]三级渐进合并策略。缓存是查询加速的核心为 Store Gateway 配置充足的 Memcached 索引缓存命中率低于 80% 时应立即扩容。对于监控规模已经突破单机 Prometheus 瓶颈的团队Mimir 是当前生态中最成熟、与 Grafana 整合度最高的大规模时序存储方案之一。