先看两个真实场景某团队接了一个 Spring Boot 应用开了 Micrometer 的 percentiles-histogram 后Grafana 面板曲线全部正常但 Prometheus 的内存从 2GB 一路涨到 6GB不到一周磁盘告警。另一头某团队的监控面板上up全绿但scrape_samples_scraped单 target 已经冲到 8 万Prometheus 的内存配额明明很高查询却越来越慢。这两个问题的答案都在同一笔账里样本数 → 序列数 → 磁盘/天 → 内存水位。本篇目标把 Prometheus 存储规划的完整账本拆一遍并提供可落地的规划建议。一、先理清三个概念指标、样本、时间序列很多人把这几个词混着用但存储规划的每一步计算都要用到它们混了就一定会算错。概念定义存储中的角色示例指标 Metric“测什么”的名称一条时间序列的名字node_cpu_seconds_total标签 Label键值对多维区分序列的唯一性由“指标名 全部标签组合”决定cpu0, modeidle时间序列 Time Series指标名 一组标签唯一确定一条序列近似等于scrape_samples_scraped磁盘和内存的最小计量单位node_cpu_seconds_total{cpu0, modeidle}样本 Sample时间序列在某个时间点的值实际写入 TSDB 的最小单元时间戳 值如1710000000000 1027四者关系一个指标可以有 N 条序列一条序列每秒或每个抓取周期产生一个样本。示例node_cpu_seconds_total指标在 8 核机器上带cpu8 个值和mode约 8 个值两个标签组合出 64 条序列。每 15 秒抓一次每分钟就产生 64 × 4 256 个样本。二、抓取一次到底产生多少样本1、内置指标抓取链路的固定开销无论 target 暴露多少业务指标Prometheus 每次抓取都会额外写入 8 个内置指标内置指标序列数指标含义up1上次抓取是否成功。2xx 且响应体可解析才记 1其余一律 0。监控体系自己的第一条告警scrape_duration_seconds1上次抓取总耗时覆盖发起请求到解析完响应体的全过程scrape_samples_scraped1上次抓取解析出的样本数metric relabel 之前的近似值scrape_samples_post_metric_relabeling1relabel 之后真正入库的样本数scrape_series_added1上次抓取中首次出现的序列数scrape_body_size_bytes1响应体未压缩大小无法获知时为 -1scrape_timeout_seconds1该 target 配置的抓取超时随配置变化scrape_sample_limit1该 target 的单次样本上限0 表示不限2、常见 exporter 的指标规模一张参考表下表汇总各 exporter 在默认配置下的指标数量全部为经验值不同版本和配置会有差异Exporter默认采集指标数约单 target 序列数约主要来源备注node_exporter800 1,0001,500 2,000cpu、meminfo、diskstats、netdev、filesystem 等 50 collector机器核数、磁盘数影响序列数Spring Boot (Micrometer/Actuator)300 500500 1,000JVM、HTTP、Tomcat、Druid、logback开了 percentiles-histogram 后基数 ×20nginx-exporter (OSS)8 exporter 自带10 50stub_status 四类指标带server_zone等标签时增加blackbox_exporter10 20 每 target10 20probe_success、probe_duration 等N 个 target ×10~20sql_exporter取决于自定义 SQL每条 SQL 标签组合业务数据由 collector 定义必须受控标签基数 ≤ 10³mysqld_exporter800 1,2001,500 2,000连接数、慢查询、缓存命中、InnoDB版本差异较大redis_exporter300 500500 800内存、命中率、客户端数多实例时按addr标签区分关键结论node_exporter 和 mysqld_exporter 是最“重”的单 target 序列数轻松破千Spring Boot 应用在默认配置下可控一旦开启 percentiles-histogram20 倍基数翻倍是常态业务类 exportersql_exporter的序列数完全由SQL 决定是唯一可以“主动控制”的部分。3、用两个内置指标对账scrape_samples_scraped是解析出的样本数scrape_series_added是“第一次见到”的序列数。两者关系scrape_samples_scraped ≈ 该 target 的序列总数近似 scrape_series_added 本次抓取新增的序列数三、存储计算公式样本怎么变成字节1、TSDB 压缩后的样本大小Prometheus 本地 TSDB 采用 Gorilla 压缩压缩后每个样本约 1.5 2 字节。这是全篇唯一一个需要记住的常数。磁盘/天 序列数 × (86400 / 抓取间隔秒) × 样本字节数(2)单位换算项公式每 target 序列数scrape_samples_scraped近似每 target 样本/秒序列数 ÷ 抓取间隔秒每 target 磁盘/天样本/秒 × 86400 × 2 字节全部 target 汇总Σ每 target 磁盘/天2、一套验证环境的完整算账以下按本系列验证环境的规模算一笔账来源序列数约抓取间隔样本/秒磁盘/天5 台主机node_exporter4,000 × 5 20,00015s1,333230 MB3 个 Spring Boot 应用4,500 × 3 13,50015s900155 MB直方图开启后×20 基数90,000 × 3 270,00015s18,0003.1 GBnginx 入口3 个实例50 × 3 15015s101.7 MBblackbox10 个 target15015s101.7 MB没开直方图时全部加起来约390 MB/天15 天保留期也就 6 GB本地盘完全够直方图一开3.1 GB/天15 天就是 46 GB立刻变成了另一个量级。核心结论很多“需要长期存储”的场景先做减法比先上架构更有效。3、内存的计算比磁盘更先爆磁盘只是最直观的内存往往是更早触顶的那一个。Prometheus 的内存主要消耗在内存水位 ≈ 活跃序列数 × 每个序列的内存开销约 1 4 KB序列数估算内存10 万100 400 MB50 万500 2 GB100 万1 4 GB300 万3 12 GB⚠️ 注意Prometheus 官方建议单节点活跃序列数不要超过 100 万超过后内存和查询延迟会迅速恶化。查询时的临时内存还要额外叠加range query 扫 30 天数据序列数过万后直接打爆内存是常事。四、单 Prometheus 能支持多少 target1、三个约束条件不是“能连上”就是“能支持”。单 Prometheus 的 target 容量受三个约束共同决定取最小值约束临界值超出后果序列数上限100 万活跃序列官方建议内存触顶、查询变慢、OOM抓取带宽所有 target 的样本/秒 Σ(序列数 ÷ 间隔)TSDB 写入跟不上背压丢样本磁盘吞吐样本/天 × 2 字节保留期缩水或直接写满2、反推 target 数上限以最常见的 node_exporter 为例单台 8 核机器约 2,000 条序列15s 抓取间隔单 target 样本/秒 2,000 ÷ 15 ≈ 133 单 target 磁盘/天 133 × 86400 × 2 ≈ 23 MB按 100 万序列上限反推target 数上限 ≈ 1,000,000 ÷ 2,000 500 台 node_exporter如果换成 Spring Boot 应用未开直方图约 1,000 条序列target 数上限 ≈ 1,000,000 ÷ 1,000 1,000 个应用但注意上述只是序列数约束磁盘是另一道坎。500 台 node_exporter 每天产生约 11.5 GB30 天保留期需要 345 GB 磁盘。3、一张规划速查表监控规模目标数约序列数约CPU内存SSD 存储说明小规模10 305 万 15 万2 核4 GB50 GB保留 30 天足够中小规模30 10015 万 50 万4 核8 GB100 200 GB数据保留 30 天大规模100 50050 万 100 万8 核16 32 GB300 500 GB保留 30 90 天需监控自身极限场景500估算 100 万8 核32 GB1 TB理论上限需分层抓取 裁剪五、规划建议先做减法再做加法1、减法一分层抓取间隔不是所有指标都需要 15s 精度。告警类指标用 15s趋势类指标用 60s样本量直接降 75%# 同一个 exporter两个 job 两档间隔-job_name:node-detail# 告警用15sscrape_interval:15sfile_sd_configs:[{files:[/etc/prometheus/targets/node.yml]}]-job_name:node-archive# 长期趋势用60sscrape_interval:60sfile_sd_configs:[{files:[/etc/prometheus/targets/node.yml]}]2、减法二丢弃确定不看的序列用metric_relabel_configs在采集末端裁剪它发生在relabel_configs之后、样本入库之前metric_relabel_configs:# node_exporter 的 Go runtime 指标对业务无用直接丢-source_labels:[__name__]regex:go_(gc|memstats|threads|info)_.*action:drop# 只保留 2xx/5xx 状态码维度的桶1xx/3xx 不看-source_labels:[status]regex:1..|3..action:drop⚠️ 注意drop 是不可逆的宁可保守。删掉一条序列历史数据一起消失。规则上线前先在验证环境跑一周确认没有面板和告警引用这些序列再全量铺开。以下是常用 exporter 的丢弃方式参考1几乎所有 exporter 都有的Go 运行时指标绝大多数官方 exporternode、mysqld、redis、blackbox 等都是 Go 写的默认会暴露go_*和promhttp_*前缀的进程自监控指标这些对监控业务毫无价值可直接丢弃。但针对 Prometheus Server不能丢弃比如process_resident_memory_bytes指标常用来监控 Server 端内存消耗情况。metric_relabel_configs:-source_labels:[__name__]regex:go_.*|promhttp_.*# go_ 运行时、promhttp_ 采集器自身action:drop2node_exporter 可丢弃序列参考node_exporter 是序列量的最大来源也是可裁剪空间最大的一个。分两层整个收集器没用和收集器里个别指标没用。层一按场景禁用的收集器很多收集器只在特定环境下才有意义非该环境可整体禁用收集器说明禁用建议mdadm软件 RAID 设备无 RAID 时禁用zfsZFS 文件系统非 ZFS 环境禁用ipvsIP 虚拟服务器非负载均衡禁用schedstat调度器统计非调试场景禁用arpARP 表多数场景无用sockstatSocket 统计多数场景无用softnet软中断统计多数场景无用netstat网络统计多数场景无用禁用方式node_exporter 启动参数node_exporter\--no-collector.arp\--no-collector.ipvs\--no-collector.sockstat\--no-collector.softnet\--no-collector.mdadm\--no-collector.zfs\--no-collector.schedstat层二虚拟文件系统序列大户中的大户即使保留filesystem收集器挂在/dev、/proc、/sys以及 Docker/Kubelet 目录下的虚拟文件系统会产生大量几乎无用的序列必须排除node_exporter\--collector.filesystem.mount-points-exclude^/(dev|proc|sys|var/lib/docker/.|var/lib/kubelet/.)($|/)\--collector.filesystem.fs-types-exclude^(autofs|binfmt_misc|bpf|cgroup2?|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|iso9660|mqueue|nsfs|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|selinuxfs|squashfs|sysfs|tracefs)$层三relabel 丢弃特定指标对于没有 exclude 参数、又确定不用的指标用metric_relabel_configs精确丢弃metric_relabel_configs:# 丢弃 IPv6 网络统计、conntrack、时间校验等低价值指标-source_labels:[__name__]regex:node_(nf_conntrack_stat|netstat_.*6|timex_pps|network_carrie|network_iface|scrape).*action:drop3nginx-prometheus-exporter 保留序列参考metric_relabel_configs:# 只保留需要的指标-source_labels:[__name__]regex:nginx_up|nginx_exporter_build_info|nginx_connections_*|nginx_http_requests_totalaction:keep4blackbox-exporter 可丢弃序列参考blackbox_exporter 在探测失败时会导出一系列值为 0 的 phase 指标probe_http_duration_seconds{phaseconnect} 0等会严重干扰聚合分析。metric_relabel_configs:# 当 probe_success 0 时丢弃所有 probe_http_duration_seconds 序列-source_labels:[__name__,probe_success]separator:regex:probe_http_duration_seconds0action:drop3、减法三管住标签基数标签基数失控是序列数爆炸的头号原因。单指标标签基数 ≤ 10³多标签乘积 ≤ 10⁴绝不把user_id、order_id、trace_id放进标签SQL 内先 GROUP BY 收敛维度再返回。用scrape_samples_post_metric_relabeling / scrape_samples_scraped看 relabel 砍掉的比例长期偏高要回头审规则用scrape_samples_scraped环比对比 1 天前识别基数漂移。4、加法远程存储留后路做完减法仍不够需要全局查询、跨多套 Prometheus 聚合、自动降采样时再考虑 remote_write 到 VictoriaMetrics / Thanos / Mimir。六、小结本篇核心要点三个概念先分清。 指标、样本、时间序列是三件事指标决定“测什么”标签组合决定“序列数”样本是磁盘和内存的最小计量单位。混着用一定算错账。固定开销要计入。 每个 target 每次抓取固定产生 8 条内置序列这是监控自己的税不随 exporter 变化。公式只有一条。 磁盘/天 ≈ 序列数 × (86400 ÷ 抓取间隔) × 2 字节内存用水位 ≈ 序列数 × 1 4 KB。按常用 exporter 的序列数对照表代入即可。先做减法再做加法。 分层抓取间隔、metric_relabel 裁剪、标签基数管控三招能救回大半块磁盘还不够再上远程存储remote_write 是成本最低的保留期延长手段。监控自身是底线。 务必监控 Prometheus 的process_resident_memory_bytes和磁盘使用率否则会在“Grafana 曲线停更”时才发现磁盘已满而那个判断本身也依赖 Grafana 可用故障是自指的。