Prometheus TSDB 拆不出来、也存不了一年:4 条外部存储出路 + 容量测算

📝 摘要 :读者问:Prometheus 的 TSDB 能独立部署吗?存更多数据有限制吗?答案是不能------TSDB 是嵌入式库,data 目录一个 lock 文件就挡住了双进程,官方明确「不集群、不副本、不支持 NFS」。但存储能换。本文含 TSDB 写入路径、官方公式算出「50 万 series 存 90 天要 518 GB」、remote_write 三个必踩的坑(远端挂 2 小时就永久丢数据),以及加盘 / VictoriaMetrics / Thanos / Mimir 四条路怎么选。
有人问了我两个问题,问得很准:

  1. Prometheus 的存储 TSDB,可以独立部署、独立维护吗?
  2. 如果要存更多的数据,有没有限制?

这两个问题背后是同一个真实困境:监控刚搭起来时谁都不管存储,--storage.tsdb.path 指个目录就上线了。跑上半年,盘满了、Grafana 拉一个月的曲线要转十几秒、想看去年同期的容量趋势发现数据早没了------这时候才回头问「存储这块能不能单独拎出来搞」。

先把结论摆出来:第一个问题的答案是「不能」,而且拦住你的是一个具体的文件;第二个问题的答案是「有,而且是三条硬限制」。但「TSDB 拆不出来」不等于「存储换不掉」------这两件事被混为一谈,是绝大多数人卡住的地方。

📚 前置阅读 :本文只讲存储 这一层,不重复搭建过程。Prometheus + Grafana + AlertManager 怎么装、Exporter 怎么配,见 《Prometheus + Grafana + AlertManager 监控体系搭建:Docker 一把梭》------那篇里 --storage.tsdb.path=/prometheus/data 只是一行启动参数,本文补上它背后的全部内容。


📖 先约定五个词

后文反复出现,先花一分钟对齐。这五个词分不清,容量就一定算错(尤其前两个,混用是最常见的错源)。

是什么 一句话理解
series(时间序列) 一个指标名 + 一组 label 的唯一组合 http_requests_total{method="GET",code="200"}{method="POST",code="200"}两条 series。它决定内存
sample(样本) 一条 series 在某个时刻的一个数据点(时间戳 + 值) 每抓取一次,每条 series 产生 1 个 sample。它决定磁盘
基数(cardinality) 一个 label 可能取值的个数 method 基数约 5;userId 基数是百万级------后者放进 label 就是灾难,见 §3.5
block(数据块) 一段时间范围的数据打包成的只读目录 默认 2 小时一个,后台会合并成更大的。「只读」这条性质是后面所有方案的地基
WAL(预写日志) 落盘前先顺序追加的日志,用于崩溃恢复 跟 MySQL redo log、PostgreSQL WAL 是同一思路。但 Prometheus 的 WAL 还兼了第二个身份,见 §4.1

💡 一句话记住两者的分工series 数量压内存,sample 数量压磁盘。 缩短保留时间只省磁盘、一点内存都省不了;治基数才省内存。这条搞反的话,后面所有优化都会做在错的地方。


一、先把两个问题正面回答了

不绕弯子,这一节直接给答案,原理和方案放后面展开。

1.1 能不能独立部署 TSDB?不能,拦你的是一个 lock 文件

很多人默认「TSDB」是个像 MySQL 那样的独立数据库服务,只是被 Prometheus 用着。不是。

Prometheus 的 TSDB 是一个编译进 Prometheus 二进制里的 Go 库prometheus/prometheus/tsdb 包),它:

  • 没有独立的可执行程序 ------没有 tsdb-server 这种东西可以单独起
  • 没有网络协议------不监听端口,不接受连接,只有 Go 的函数调用
  • 没有认证、没有多客户端概念------它压根不是设计给「外部访问」的

也就是说,「把 TSDB 单独部署到一台存储机、让 Prometheus 远程连过去」这个想法,在架构上不存在实现路径

那能不能退一步:Prometheus 还在原地,只把 data 目录挂到网络存储(NFS / EFS)上,让存储独立? 也不行,而且这条路踩下去是数据损坏级别的后果。官方文档写得非常直白:

Non-POSIX compliant filesystems are not supported for Prometheus' local storage as unrecoverable corruptions may happen. NFS filesystems (including AWS's EFS) are not supported.

------ Prometheus 官方文档 · Storage

「unrecoverable corruptions」(不可恢复的损坏) ,用词已经很重了。原因就在于 TSDB 大量依赖 POSIX 文件锁和 mmap 语义,而 NFS 这类网络文件系统对这两样的实现是打折的。

再退一步:两个 Prometheus 共享一个 data 目录做高可用? 这是最直接撞墙的一条------TSDB 会在 data 目录里放一个 lock 文件独占,第二个进程起不来,直接报错退出:

复制代码
level=error caller=main.go:894 err="opening storage failed: lock DB directory: resource temporarily unavailable"

这个报错在社区里出现频率很高,通常是两种场景撞上的:Kubernetes 里 Prometheus 副本数写成了 2 且共享同一个 PVC,或者旧进程没退干净就拉了新的。

⚠️ 别去找 --storage.tsdb.no-lockfile 这个「解法」。 这个参数确实存在,源码里的说明只有一句大白话------Do not create lockfile in data directory.cmd/prometheus/main.go)。它是给某些部署环境本身已经保证了单实例 的场景用的逃生舱(比如编排层已经锁死副本数、或数据目录挂载方式导致锁文件创建不了),不代表 Prometheus 支持多实例共享目录

拿它去让两个正常 Prometheus 共写一个目录,等于亲手把上面那句 unrecoverable corruptions 兑现------锁没了,但两个进程同时写 WAL、同时做 compaction 的问题一个都没解决,锁只是原本在替你挡着而已。

📌 顺带澄清一个容易混的点:源码里其实有两个同名参数,分属两种模式、互不通用------

参数 作用域 何时可用
--storage.tsdb.no-lockfile serverOnlyFlag 只在普通 server 模式下可用
--storage.agent.no-lockfile agentOnlyFlag 只在 Agent 模式下可用(§4.4)

所以别把前者理解成「给 Agent 模式准备的」------它俩是平行的两个开关,用错模式直接不认这个参数。

顺着往下,官方还有一句更根本的定性:

A limitation of local storage is that it is not clustered or replicated.

------ Prometheus 官方文档 · Storage

不做集群、不做副本。 这不是「暂时还没做」,而是 Prometheus 明确划出去的边界------它把自己定位成一个单机、自包含、可靠性靠简单换来的采集与告警引擎,长期存储和水平扩展交给生态里的其他项目。

所以「独立部署存储」的正确形态不是拆 TSDB,而是换掉存储的去向 :让 Prometheus 通过 remote_write 把数据吐给一个真正独立部署的时序数据库,TSDB 退化成一个短期缓冲。这是 §四、§五的主题。

1.2 能存多少?三条硬限制,先看清哪条会先撞

「存更多数据有没有限制」------有,而且是三条不同性质的限制,触发条件也不一样。多数人只盯着磁盘,结果被内存先干掉。

# 限制 由什么决定 撞上的表现 能不能靠加机器解决
磁盘容量 单机本地盘大小 × 保留时间 盘满,Prometheus 停止写入 ❌ 不能横向扩,只能换更大的盘
内存 活跃 series 数量(不是磁盘量!) OOM 被杀,重启后重放 WAL 又慢 ❌ 只能垂直加内存
查询能力 原生没有降采样 查一年的图要扫全部原始点,慢甚至打爆内存 ❌ 加机器也没用,是设计缺失

三条里最容易被忽视的是 ②③:

  • ② 内存跟着 series 走,不跟磁盘走。 你把保留时间从 90 天砍到 15 天,磁盘立刻降到 1/6,内存一点都不会降 ------因为内存里装的是「当前活跃的 series」,跟你留多久历史无关。反过来,某个应用上线时在 label 里塞了 traceId,磁盘还没什么动静,内存已经先炸了。
  • ③ 没有降采样是原生 Prometheus 的结构性缺失。 存了一年的数据,查「过去一年的 CPU 趋势」时它会老老实实去扫一年里每 15 秒一个的原始点。Thanos / Mimir 会把老数据预聚合成 5 分钟、1 小时粒度(一年的图本来也看不出 15 秒的细节),原生 TSDB 没这个能力------这也是「存得下」和「查得动」是两个问题的原因

💡 一句话 :磁盘不够是成本问题 (加盘就行),内存不够和查不动是架构问题 (必须换方案)。先判断自己撞的是哪一条,别拿加盘去治基数爆炸。


二、TSDB 长什么样:搞清结构,才明白为什么拆不开、又为什么能搬走

上一节给了结论,这一节讲清「为什么」。看懂目录结构和写入路径,后面 Thanos 为什么能把数据搬到 S3、remote_write 为什么会丢数据,就都是顺理成章的了。

2.1 目录结构:每个文件是干什么的

官方给出的 data 目录结构(Storage 文档),加上实际会看到的 lock

复制代码
./data
├── 01BKGV7JBM69T2G1BGBGM6KB12/     # 一个 block(目录名是 ULID,含时间信息、可排序)
│   └── meta.json
├── 01BKGTZQ1SYQJTR4PB43C8PD98/     # 另一个 block
│   ├── chunks/
│   │   └── 000001                  # 压缩后的样本数据,每段最大 512MB
│   ├── index                       # 倒排索引:label 键值 → series → chunk 位置
│   ├── meta.json                   # 时间范围、series 数、压缩层级
│   └── tombstones                  # 删除标记(删数据不立即物理删,见下方说明)
├── chunks_head/                    # head block 已满 chunk 的落盘镜像(mmap 读取)
│   └── 000001
├── wal/                            # 预写日志,每段 128MB
│   ├── 000000002
│   └── checkpoint.00000001/        # WAL 检查点,压缩掉已落 block 的部分
│       └── 00000000
└── lock                            # ⭐ 独占锁,就是它让 TSDB「一个目录只能一个进程」

三个值得单独说的:

  • meta.json 里的 compaction.level :block 是分层合并的。2 小时的小 block 会被后台 compactor 合并成更大的 block(level 1 → 2 → 3......),减少查询时要打开的文件数。合并只发生在本地,且是重写而非追加
  • tombstones(墓碑) :调 delete API 删数据时,Prometheus 不立即物理删除,只写一个墓碑标记,查询时跳过。真正的空间回收要等下一次 compaction。所以「删了数据盘没变小」是正常现象,不是 bug。
  • lock :社区反馈它是基于 PID 而非 flock() 的,所以有个边缘坑------进程被强杀后留下陈旧 lock,若那个 PID 恰好被别的程序占用了,Prometheus 会误判「数据库被锁」而拒绝启动。遇到起不来先确认没有活着的 Prometheus,再清理。

2.2 一条指标从抓取到落盘,走了什么路

按图上的编号:

  1. scrape :按 scrape_interval 去 Exporter 拉一次 /metrics
  2. 同时写两处 ------这是关键:
    • 内存里的 head block(当前正在写的那个 2 小时窗口),查询走这里,最快;
    • 追加进 WAL ,纯顺序写,只为一件事:进程崩了能恢复
  3. chunks_head 落盘 :head 里写满的 chunk 会刷到 chunks_head/,之后通过 mmap 访问。这一步让 head 不必把全部数据都常驻堆内存,是内存优化的关键机制,也是为什么「内存占用 < 活跃数据总量」。
  4. 切 block :head 攒满 2 小时,整体持久化成一个 01BKG.../ 目录(chunks + index + meta.json),从此只读,永不修改。对应的 WAL 段被 checkpoint 掉。
  5. compact:后台把相邻小 block 合并成大 block,同时按 retention 策略删掉过期的整个 block 目录。

🔑 第 4 步的「从此只读」是全文最重要的一句话。 正因为 block 一旦生成就不可变,它才能被安全地整目录复制走 ------Thanos 的 sidecar 干的就是这件事:盯着 data 目录,发现新 block 就上传到对象存储。如果 block 是可变的,这种「旁路搬运」根本不成立。

这也解释了 §1.1 的另一半:block 可以搬走,但 head + WAL + lock 这套「正在写的状态」搬不走------所以能外接存储,不能外接 TSDB。

2.3 跟 MySQL / PostgreSQL 的 WAL 是一回事吗

思路一致,用途有个重要差别。

MySQL redo log / PostgreSQL WAL Prometheus WAL
崩溃恢复 ✅ 主要用途 ✅ 主要用途
主从复制的数据源 ✅ binlog / WAL 流式发给备库 ⚠️ 不做复制 ,但 remote_write 从 WAL 读数据往外发(§4.1)
能不能靠它做时间点恢复(PITR) ✅ 可以 ❌ 不能,Prometheus 没有 PITR 概念
保留多久 可配置,可以很长 约 2 小时,之后被 checkpoint 压缩掉

最后一行是个会咬人的差别 :Prometheus 的 WAL 只留约 2 小时。而 remote_write 恰好是从 WAL 读数据的------所以远端存储挂超过 2 小时,那部分数据就永久没了,不是延迟送达,是丢。这条坑在 §4.2 详细展开。

💡 对 WAL、复制、故障切换这一整套机制想系统看的,可以参考【待发布后补充引用关系:《PostgreSQL 高可用理论篇:一主一从不等于高可用,从流复制到 Patroni 自动切换》】------那篇把 WAL 的三段记账(write / flush / replay)、同步档位、复制槽讲透了,很多概念跟这里是通的。


三、先别急着换方案:把原生 TSDB 用到极限

很多人一听「存不下」立刻要上 Thanos,结果引入 5 个组件、还得配对象存储,最后发现真实问题是某个应用往 label 里塞了动态值。换存储治不了基数爆炸,顺序别搞反。

3.1 两个 retention 阀门:谁先到谁生效

bash 复制代码
prometheus \
  --storage.tsdb.path=/prometheus/data \
  --storage.tsdb.retention.time=30d \      # 按时间:留 30 天
  --storage.tsdb.retention.size=200GB      # 按大小:最多占 200GB
参数 默认值 行为
--storage.tsdb.retention.time 15d(两个都不设时生效) 超过时长的 block 整个删掉
--storage.tsdb.retention.size 0(关闭) 超过容量就从最老的 block 开始删

两个同时配,谁先触发谁生效(逻辑上取更严格的那个)。

生产建议:两个都配。 只配 time 的风险是------某天业务上线一批新指标,series 翻倍,磁盘在 30 天没到之前就满了,Prometheus 直接停止写入,监控系统自己先瞎了 。加一条 retention.size 当保险,宁可少留几天历史,也不能让监控自己挂掉。

设置时留出余量:retention.size 给到盘容量的 70~80%,别顶到 100%------compaction 过程中会临时占用额外空间(要同时存在旧 block 和正在写的新 block)。

3.2 容量测算:官方公式 + 一张能直接查的表

官方给的公式很简洁:

复制代码
needed_disk_space = retention_time_seconds × ingested_samples_per_second × bytes_per_sample

其中 bytes_per_sample,官方的说法是:

Prometheus stores an average of only 1-2 bytes per sample.

这个「1~2 字节」低到反直觉------一个时间戳加一个 float64 明明是 16 字节。能压到这个量级靠的是 Gorilla 压缩算法 (Facebook 的时序压缩论文):时间戳存差值的差值(等间隔抓取时几乎为 0),数值存与前值的 XOR(监控指标相邻值通常很接近,XOR 后高位全是 0)。指标越平稳,压缩比越高------这也意味着剧烈抖动的指标会比预估占更多空间。

ingested_samples_per_second 可以从 series 推出来:

复制代码
samples_per_second = 活跃 series 数 ÷ scrape_interval(秒)

scrape_interval=15sbytes_per_sample=2(取保守上限)算出的对照表

活跃 series samples/s 留 15 天 留 30 天 留 90 天 留 180 天 留 365 天
10 万 6.7k 17 GB 35 GB 104 GB 207 GB 420 GB
50 万 33k 86 GB 173 GB 518 GB 1.04 TB 2.1 TB
200 万 133k 346 GB 691 GB 2.07 TB 4.15 TB 8.4 TB

⚠️ 实际还要再乘 1.2 左右,给 index、meta.json、tombstones 这些非样本数据留量。上表是纯样本估算,规划时按 ×1.2 报预算。

这张表把「存不下」量化了。最该被看见的是中间那行

50 万 series 是个中型系统很容易到的量级 (几十个服务 + 十几种 Exporter 就够了)。它留 15 天只要 86 GB,一块盘轻松装下;但想留 90 天就要 518 GB,留一年要 2.1 TB ------而且这 2.1 TB 全压在一块本地盘上,还没算查询时的内存开销。

这就是原生 TSDB 的天花板具体长什么样:不是「不能存」,是「存到一年这个量级,单机方案在成本和风险上都不再合理」。

3.3 别猜 bytes_per_sample,量一下你自己的

上面用的是 2 字节保守值,但你的实际值可能是 1.1 也可能是 3------取决于指标平稳程度。直接在 Prometheus 里查真实值,比套公式准得多:

promql 复制代码
# 你的实际 bytes per sample
rate(prometheus_tsdb_compaction_chunk_size_bytes_sum[1h])
  /
rate(prometheus_tsdb_compaction_chunk_samples_sum[1h])
promql 复制代码
# 你的实际样本摄入速率(samples/s)
rate(prometheus_tsdb_head_samples_appended_total[1h])
promql 复制代码
# 当前活跃 series 数------决定内存的那个数字
prometheus_tsdb_head_series

拿这三个实测值代回 §3.2 的公式,就能算出贴合自己系统的 磁盘需求。规划容量前先跑这三条查询,别拿别人博客里的数字当自己的预算依据(包括本文这张表)。

⚠️ 查不到这几个指标是正常的------prometheus_tsdb_* 这组自监控指标跟版本、部署方式都有关。 别急着以为公式错了,先确认你这套里到底有哪些:

promql 复制代码
{__name__=~"prometheus_tsdb_compaction.*"}   # 这组实际存在的名字,一次列全

或者直接看原始暴露:curl -s http://<你的prometheus>:9090/metrics | grep tsdb_compaction
按查出来的结果对号入座:

  • 一条都没有 → 多半是 Prometheus 没抓自己prometheus.yml 里要有一个指向自己 :9090 的 job(见 Prometheus + Grafana + AlertManager 监控体系搭建 里的 job_name: 'prometheus')。没这一条的话,所有 prometheus_ 开头的指标都查不到 ,不只是这几个。另外 Agent 模式(§4.4)本身就没有本地 TSDB,自然也没有 compaction 这组。
  • 名字能列出来,但查 _sum 是空的compaction 还没跑过 。head block 默认攒够约 2 小时才落盘压缩一次,刚起的实例一次观测都没有;此时 rate(...) / rate(...) 会算出 NaN 被 PromQL 丢掉,表现就是 Empty query result等它跑满几个压缩周期再查
  • 名字和本文对不上 → 以你查出来的为准。不同版本这组指标增删改过,本文写的是当前官方源码里的名字。
    实在拿不到,用最笨但最准的办法 :隔 24 小时量两次数据目录大小(du -sb <storage.tsdb.path>),差值除以这段时间的样本数 increase(prometheus_tsdb_head_samples_appended_total[24h]),得到的就是你自己的 bytes_per_sample。这个办法不依赖任何自监控指标,任何版本都能用。

3.4 内存怎么估:跟着 series 走

内存的经验公式(社区常引用的量级,不是官方承诺值):

复制代码
head 内存 ≈ 活跃 series 数 × 约 7.5 KiB
活跃 series head 内存估算 整机建议内存
10 万 ≈ 0.7 GiB 4 GB
50 万 ≈ 3.6 GiB 8~16 GB
200 万 ≈ 14 GiB 32 GB+

「整机建议」按 head 的 2 倍以上 给,因为还要装下:查询的临时开销 (大范围查询会把数据加载进内存,是最容易触发 OOM 的动作)、remote_write 的发送队列 (§4.1,队列内存 ∝ 分片数 × 队列容量)、compaction 的工作内存、以及给 OS page cache 留的余量(block 靠 mmap 读,page cache 直接决定查询快慢)。

同样,别用估算值,量自己的:

promql 复制代码
prometheus_tsdb_head_series                    # 活跃 series
process_resident_memory_bytes{job="prometheus"} # 实际内存占用

💡 第二条里的 job="prometheus" 要换成你自己 prometheus.yml 里那个 job 的名字 ,写错了同样是空结果。查不到就先去掉标签选择器:process_resident_memory_bytes,看看实际有哪些 job 值。上面 §3.3 那段「查不到怎么办」对这里同样适用。
⚠️ 重启慢也是内存问题的连带后果 :Prometheus 启动时要重放 WAL 才能重建 head。series 越多、WAL 越大,重启就越慢------大实例重启几分钟起步,这段时间没有采集、没有告警评估。所以给 Prometheus 留足内存不只是防 OOM,也是在压缩这段监控盲区。

3.5 ⚠️ 换存储治不了的病:基数爆炸

这是「存储不够」最常见的假象 。真正的病根往往是某个 label 里塞了不该塞的动态值

promql 复制代码
# ❌ 灾难写法:每个用户产生一条独立 series
http_requests_total{userId="10086", path="/api/order"}

# ❌ 更狠的:traceId 基数无上限,每个请求都是一条新 series
db_query_duration{traceId="a1b2c3d4...", sql="SELECT..."}

# ✅ 正确:label 只放低基数的枚举值
http_requests_total{method="GET", path="/api/order", code="200"}

为什么它比「数据太多」严重得多 :series 数量是乘法关系 。一个指标带 5 个 label,各自基数是 10、5、20、3、2,就是 10×5×20×3×2 = 6000 条 series。其中任何一个 label 换成 userId(百万基数),这个指标单独就能产生百万级 series ------内存直接爆,而且换成 VictoriaMetrics 或 Thanos 一样爆,只是爆得晚一点。

怎么定位真凶

promql 复制代码
# 哪些指标名 series 最多(先看这个)
topk(10, count by (__name__)({__name__=~".+"}))

# 某个可疑指标里,哪个 label 是基数元凶
count(count by (userId) (http_requests_total))

更省事的办法是直接开 Web UI 的 /tsdb-status 页面(Status → TSDB Status),Prometheus 自带一组统计:series 最多的指标名、基数最高的 label、占空间最多的 label 值。比自己写 PromQL 快,排查基数问题第一站就该看这里。

治法只有一条:从源头改埋点 ,把高基数值从 label 挪走------真要按 userId 排查就该去日志和链路追踪系统查(那才是它们的战场),指标系统只该存可聚合的低基数维度

💡 这个错误的形状,和【待发布后补充引用关系:《ClickHouse 大表 67 GiB 清到 57 MiB:真凶不是重复行,是被重存 N 遍的数组》】里的病根是同一类:都不是「数据本来就这么多」,而是把不该存进来的东西存进来了。 遇到存储暴涨,先怀疑「存了什么」,再怀疑「存了多久」------顺序反了就会拿加盘去治设计问题。

3.6 什么信号说明真该换方案了

把上面的手段(调 retention、治基数、加盘)都用过之后,出现下面任意一条,就是原生 TSDB 到顶了:

信号 具体表现 说明
合规 / 业务要长期数据 要看季度环比、年度容量趋势、要留一年以上 最硬的一条,加盘也解决不了「查得动」
活跃 series 稳定超百万 且已确认不是基数问题、是真实业务规模 内存进入危险区,垂直扩容边际成本陡增
查长周期直接把 Prometheus 打挂 拉 30 天的图 OOM 或超时 缺降采样,架构缺陷
多个 Prometheus 要统一查询 分机房 / 分集群各一套,想在一个看板里看全局 单机 TSDB 天生没有全局视图
监控自己成了单点 Prometheus 挂了这段时间数据永久空洞 官方明确「不集群不副本」,本机存储无解

反过来------活跃 series 在百万以内、只需要留 15~30 天、单套 Prometheus ,那么加盘 + 配好两个 retention 阀门就够了,别上 Thanos。多引入的组件本身就是新的故障源,而监控系统挂掉的代价是「所有故障你都看不见」。


四、外部存储:remote_write 是唯一正道

确认要换了,接口只有一个:remote_write。它就是官方留出的「存储换出去」的口子。

4.1 remote_write 干的其实是「读 WAL 再转发」

配置本身很简单:

yaml 复制代码
# prometheus.yml
remote_write:
  - url: "http://victoriametrics:8428/api/v1/write"
    queue_config:
      capacity: 10000            # 单个分片的队列容量
      max_shards: 50             # 最大并发分片数(默认 200)
      min_shards: 1
      max_samples_per_send: 2000 # 每个请求带多少样本
      batch_send_deadline: 5s    # 攒不满也最多等这么久
      min_backoff: 30ms
      max_backoff: 5s
    # 可选:只把需要长存的指标发出去,减少远端压力和成本
    write_relabel_configs:
      - source_labels: [__name__]
        regex: 'go_.*|process_.*'   # 过滤掉进程自身的指标
        action: drop

关键在于它的工作机制 ,官方 Remote write tuning 描述得很清楚:

Each remote write destination starts a queue which reads from the write-ahead log (WAL), writes the samples into an in memory queue owned by a shard, which then sends a request to the configured endpoint.

remote_write 是一个 WAL 的读者。 它不是在采集时旁路分流,而是数据先正常落进 WAL,再由独立的队列去读 WAL、分片、批量发往远端。

这个机制直接解释了下一节三个坑的全部成因。

4.2 ⚠️ 三个必须知道的坑

坑 1:remote_write 不省本地盘

最常见的误解 :以为配了 remote_write,数据就「转移」到外部了,本地不占空间。

不是。 从上图看得很清楚:本地的 head → block → compact 这条路照常走remote_write 只是额外 多了一条发送路径。配完之后本地磁盘占用一点没变,反而因为多了发送队列,内存还涨了

想省本地盘必须自己动手 ------配了 remote_write 之后,把本地 retention 主动调短:

bash 复制代码
--storage.tsdb.retention.time=6h   # 历史数据在远端,本地只留短期

但短到多少有个下限,就是坑 3。

坑 2:远端挂超过 2 小时 = 数据永久丢失

这条最危险,因为它平时完全看不出来,出事时已经晚了。官方原话:

Failures will be retried without loss of data unless the remote endpoint remains down for more than 2 hours. After 2 hours, the WAL will be compacted and data that has not been sent will be lost.

因果链是这样的:remote_write 从 WAL 读数据 → WAL 只保留约 2 小时就被 checkpoint 压缩 → 远端宕机超过 2 小时,还没发出去的数据随 WAL 一起被清掉

这不是延迟送达,是永久空洞。 而且本地 block 里那份也会按你配的 retention 到期删除------如果按坑 1 把本地调到了 6h,那么远端挂一个周末,这两天的数据就两边都没有了。

必配的告警(这条必须有,没有等于在裸奔):

yaml 复制代码
groups:
- name: remote-write
  rules:
  # ⭐ 最关键:样本被丢弃,说明队列满了、正在丢数据
  - alert: RemoteWriteSamplesDropped
    expr: rate(prometheus_remote_storage_samples_dropped_total[5m]) > 0
    for: 5m
    labels: { severity: critical }
    annotations:
      summary: "remote_write 正在丢样本,远端可能已挂或太慢"

  # 落后越来越多,是丢数据的前兆------留出的处置时间比上面那条多
  - alert: RemoteWriteBehind
    expr: |
      (prometheus_remote_storage_highest_timestamp_in_seconds
       - ignoring(remote_name, url) prometheus_remote_storage_queue_highest_sent_timestamp_seconds)
      > 300
    for: 10m
    labels: { severity: warning }
    annotations:
      summary: "remote_write 落后远端超过 5 分钟"

  # 分片打到上限,说明吞吐不够,该调 max_shards 或扩远端
  - alert: RemoteWriteShardsMaxed
    expr: |
      prometheus_remote_storage_shards
      >= prometheus_remote_storage_shards_max
    for: 15m
    labels: { severity: warning }

💡 prometheus_remote_storage_samples_dropped_total 一旦有增长就是在丢数据,这条按 critical 配,别按 warning------它响的时候,你损失的是永远补不回来的监控数据。

坑 3:告警规则还在本地评估,本地 retention 有下限

容易被漏掉、后果很隐蔽的一条。

remote_write 只管把数据往外送,Prometheus 的告警规则和 recording rules 仍然在本地评估、读的是本地 TSDB。所以:

yaml 复制代码
# 假设你有这么一条规则
- alert: HighErrorRate
  expr: rate(http_errors_total[1h]) / rate(http_requests_total[1h]) > 0.05
  for: 30m

这条规则需要本地至少有 1 小时的数据[1h] 窗口),加上 for: 30m 的持续判断,实际需要更多。如果你按坑 1 把本地 retention 压到 30m这条告警会静默失效------不报错、不告警、看起来一切正常,直到某天真出事了它没响。

规则

复制代码
本地 retention ≥ 所有规则里最长的时间窗口 × 2,再往上取整

排查方法------把所有规则里的时间窗口捞出来看最长的:

bash 复制代码
grep -rhoE '\[[0-9]+[smhd]\]' /etc/prometheus/rules/ | sort -u

保险起见本地留 24h 以上是个稳妥的默认值:既覆盖了绝大多数规则窗口,也给远端故障留出了远超 2 小时的缓冲。别为了省几十 GB 盘把告警搞瘸了。

⚠️ 想真正把规则也移出去,得用 Thanos RulerVictoriaMetrics 的 vmalert------它们在外部存储上评估规则。那才是「彻底把 Prometheus 变成纯采集器」的完整形态。

4.3 remote_read 为什么基本不用

remote_write 就有 remote_read,但它很少是正确答案

remote_read 的工作方式是:查询打到 Prometheus,Prometheus 把原始样本 从远端拉回本地,再在本地跑 PromQL 计算。问题一目了然------大范围查询要把海量原始点通过网络搬回来,慢,而且容易把 Prometheus 自己打爆。

正确做法:让 Grafana 直连外部存储。

复制代码
         ┌── Prometheus(采集 + 告警,短期数据)
Grafana ─┤
         └── VictoriaMetrics / Thanos Query(历史数据,直连查询)

VictoriaMetrics、Thanos Query、Mimir 都提供 Prometheus 兼容的查询 API,在 Grafana 里当成一个普通 Prometheus 数据源加上就行。看近期数据选前者,看历史趋势选后者;也可以直接把外部存储设为默认数据源(它通常也能查到近期数据)。

4.4 Agent 模式:最接近「把存储拆出去」的形态

如果你的诉求是「Prometheus 只负责采集,存储完全在别处」,官方给了正式支持------Agent 模式

bash 复制代码
prometheus --agent --config.file=/etc/prometheus/prometheus.yml

它的行为(官方文档):

  • ✅ 保留:scrape、服务发现、remote_write------配置文件写法完全不变
  • ❌ 去掉:本地查询("You can not query the local Prometheus instance")
  • ❌ 去掉:告警("All alerting must be done by the remote system")
  • ❌ 去掉:recording rules
  • ❌ 不生成本地 block,只用一个专门优化过的 WAL :写成功即删除数据;远端不可达时临时落盘,同样限于 2 小时缓冲
  • Web UI 在 9095 端口,能看 targets 和配置,但不能查询

这才是「TSDB 独立出去」这个诉求的官方答案 :不是把 TSDB 搬走,而是把 Prometheus 砍成一个纯转发器,存储、查询、告警全部交给远端系统。

适合 :边缘节点、每个 K8s 集群放一个采集器往中心汇聚、多机房架构。

不适合 :单套监控的主力实例------告警没了,你得先把告警体系整个搬到远端(vmalert / Thanos Ruler / Mimir Ruler)才能用它。


五、四条路线怎么选

5.1 对照表

维度 ① 原生 TSDB + 加盘 ② VictoriaMetrics ③ Thanos ④ Grafana Mimir
形态 嵌在 Prometheus 里 独立时序库,vmsingle 单二进制起步 Prometheus + 对象存储 + 一组组件 集群,微服务化
怎么接 什么都不用改 remote_write sidecar 传 block(不走 remote_write) remote_write
历史数据落在 本地盘 本地盘(集群版可分片) 对象存储(S3 / MinIO / OSS) 对象存储
组件数 1 1(vmsingle) 4~5(sidecar / store / query / compact / ruler) 10+
降采样 ❌ 没有 ⚠️ 开源版无,企业版有(以官网为准 ✅ 5m / 1h 两档 ✅ 有
查询语言 PromQL MetricsQL(PromQL 超集,绝大多数直接兼容) 原生 PromQL 原生 PromQL
压缩 基准 官方宣称可达 10x 约 2~4x + 降采样省量 与 Thanos 同量级
多租户 集群版支持 强项
运维成本 最低 中高
适合 <100 万 series、留 15~30 天 中小团队要长期存储 已有多套 Prometheus + 已有对象存储 多租户 / 超大规模 / 已在 Grafana 生态

⚠️ 压缩比和降采样的授权归属都请以官网当时的口径为准 ------这两项是选型的关键变量,而开源项目的功能划分和许可会变。上表里 VictoriaMetrics 的 10x 是官方宣称值(不是我实测的),实际压缩比取决于你的指标平稳程度(原理见 §3.2)。

5.2 决策路径

#mermaid-svg-FV7NDzjKLfJpcAwW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FV7NDzjKLfJpcAwW .error-icon{fill:#552222;}#mermaid-svg-FV7NDzjKLfJpcAwW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FV7NDzjKLfJpcAwW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FV7NDzjKLfJpcAwW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FV7NDzjKLfJpcAwW .marker.cross{stroke:#333333;}#mermaid-svg-FV7NDzjKLfJpcAwW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FV7NDzjKLfJpcAwW p{margin:0;}#mermaid-svg-FV7NDzjKLfJpcAwW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW .cluster-label text{fill:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW .cluster-label span{color:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW .cluster-label span p{background-color:transparent;}#mermaid-svg-FV7NDzjKLfJpcAwW .label text,#mermaid-svg-FV7NDzjKLfJpcAwW span{fill:#333;color:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW .node rect,#mermaid-svg-FV7NDzjKLfJpcAwW .node circle,#mermaid-svg-FV7NDzjKLfJpcAwW .node ellipse,#mermaid-svg-FV7NDzjKLfJpcAwW .node polygon,#mermaid-svg-FV7NDzjKLfJpcAwW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FV7NDzjKLfJpcAwW .rough-node .label text,#mermaid-svg-FV7NDzjKLfJpcAwW .node .label text,#mermaid-svg-FV7NDzjKLfJpcAwW .image-shape .label,#mermaid-svg-FV7NDzjKLfJpcAwW .icon-shape .label{text-anchor:middle;}#mermaid-svg-FV7NDzjKLfJpcAwW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FV7NDzjKLfJpcAwW .rough-node .label,#mermaid-svg-FV7NDzjKLfJpcAwW .node .label,#mermaid-svg-FV7NDzjKLfJpcAwW .image-shape .label,#mermaid-svg-FV7NDzjKLfJpcAwW .icon-shape .label{text-align:center;}#mermaid-svg-FV7NDzjKLfJpcAwW .node.clickable{cursor:pointer;}#mermaid-svg-FV7NDzjKLfJpcAwW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FV7NDzjKLfJpcAwW .arrowheadPath{fill:#333333;}#mermaid-svg-FV7NDzjKLfJpcAwW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FV7NDzjKLfJpcAwW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FV7NDzjKLfJpcAwW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FV7NDzjKLfJpcAwW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FV7NDzjKLfJpcAwW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FV7NDzjKLfJpcAwW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FV7NDzjKLfJpcAwW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FV7NDzjKLfJpcAwW .cluster text{fill:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW .cluster span{color:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FV7NDzjKLfJpcAwW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FV7NDzjKLfJpcAwW rect.text{fill:none;stroke-width:0;}#mermaid-svg-FV7NDzjKLfJpcAwW .icon-shape,#mermaid-svg-FV7NDzjKLfJpcAwW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FV7NDzjKLfJpcAwW .icon-shape p,#mermaid-svg-FV7NDzjKLfJpcAwW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FV7NDzjKLfJpcAwW .icon-shape .label rect,#mermaid-svg-FV7NDzjKLfJpcAwW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FV7NDzjKLfJpcAwW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FV7NDzjKLfJpcAwW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FV7NDzjKLfJpcAwW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是:某指标 series 畸高
不是,是真实规模
≤30 天
>30 天 / 要看年度趋势
单套就够
≤3 人
有专职 SRE
多集群 / 多机房

没有
还要多租户隔离
Prometheus 存储扛不住了
先查 /tsdb-status

是基数爆炸吗?
❌ 别换存储

回去改埋点,把高基数 label 挪走

(§3.5)
要留多久?
✅ 原生 TSDB

加盘 + retention.time/size 双阀

(§3.1)
已经有多套 Prometheus

需要统一查询吗?
团队有几个人

能扛运维?
✅ VictoriaMetrics

vmsingle 单进程 + vmalert

运维成本最接近原生
VictoriaMetrics 或 Thanos

看有没有现成对象存储
有对象存储

(S3/MinIO/OSS)吗?
✅ Thanos

sidecar 上传 block

历史进对象存储、成本最低
✅ VictoriaMetrics 集群版

不依赖对象存储
✅ Grafana Mimir

⚠️ 运维最重,先确认扛得住

5.3 落到具体建议

Thanos 和 VictoriaMetrics 的分水岭不是性能,是「你已经有什么」

  • 已经有一批 Prometheus 在跑、且有对象存储Thanos 。它的 sidecar 模式不改变你的采集模型:Prometheus 照旧跑,旁边挂个 sidecar 把只读 block 上传到 S3(这正是 §2.2 那句「block 从此只读」的价值兑现)。历史数据在对象存储上,成本比块存储低一个量级,还有降采样。代价是组件多、查询链路长(Query → Store Gateway → 对象存储),历史查询延迟明显高于本地盘。

  • 就一套 Prometheus、团队没有专职 SREVictoriaMetricsvmsingle单个二进制、一个进程 就能上生产,运维心智负担和原生 Prometheus 几乎一个量级,配上 vmalert 就能把告警也接过去。代价是 MetricsQL 虽然是 PromQL 超集,但社区看板偶尔会有个别函数行为差异,导入现成 Dashboard 时留意验证。

  • Mimir :横向扩展和多租户是它的强项,但 10+ 个组件的运维成本是实打实的。没有专职团队别碰------为了存监控数据引入一套比业务系统还复杂的东西,本末倒置了。

  • InfluxDB / TimescaleDB / GreptimeDB 这类通用时序库 :能接,但不在上面四条推荐里------原因值得单独说清,见下面 §5.4。

💡 最后一条,也是最容易被忽略的:换存储之前,先确认 Prometheus 自己被监控着。 Prometheus 挂了通常没人告诉你------因为告警就是它发的。至少配上「Prometheus 自身 down」「TSDB head series 突增」「remote_write 丢样本」这三条,交叉监控(两套 Prometheus 互相抓)或用 Alertmanager 的 Dead man's switch 兜底。监控系统的盲区就是它自己。

这类「组件自己出问题反而没人知道」的坑,在 《监控指标自己把进程打到 CPU 100%:一次 safepoint 风暴的深度复盘》 里有个更极端的版本------监控埋点本身成了故障源

5.4 为什么不用 InfluxDB?

这是被问最多的一个问题------InfluxDB 名气比 VictoriaMetrics 大得多,很多人第一反应就是它。它不是坏数据库,但它不是「Prometheus 长期存储」这个岗位的合适人选。

先说清判断标准:选长期存储时,你真正在买的不是「数据库好不好」,而是「PromQL 还能不能用」。因为 PromQL 决定了三样已经存在的资产还认不认:

已有资产 用 VictoriaMetrics / Thanos / Mimir 用 InfluxDB
Grafana 看板(含所有社区看板 ID) ✅ 直接用 ❌ 全部重写查询
告警规则.rules 文件) ✅ 原样交给 vmalert / Thanos Ruler ❌ 重写成它自己的告警体系
团队会写的查询语言 ✅ 还是 PromQL ❌ 学 InfluxQL / SQL

这三行就是全部理由。展开说四点:

① 没有原生 PromQL

Prometheus 生态里几乎所有现成资产都是 PromQL 写的。你在 Grafana 里按 ID 导入的那些看板(node_exporter、MinIO、Redis、Kafka......),查询全是 PromQL。换到 InfluxDB,这些一张都不能直接用------要么逐条重写,要么放弃社区看板自己画。

社区里早有人提过「希望原生支持 PromQL,这样现有看板不用改」这个需求,理由跟这里说的完全一样。截至写这篇时没有看到官方原生 PromQL 支持 ;如果你正在评估,这一条务必去官网确认当前状态------它一旦支持,下面大半结论都要重估。

② 数据模型对不上,翻译是有损的

Prometheus InfluxDB
结构 指标名 + labels(全是字符串)+ float64 measurement + tags (索引)+ fields(有类型)+ 时间戳

写入方向还行(指标名→measurement、labels→tags、值→一个叫 value 的 field),但读的方向就别扭了 :本来一句 rate(http_requests_total[5m]) 的事,要用它的语言重新表达一遍聚合逻辑。而且这个映射是 Prometheus 模型的降级投射------它的 fields 支持多类型、多字段,Prometheus 用不上;反过来 Prometheus 要的那套 PromQL 语义,它没有。

③ 版本反复带来的真实迁移成本(我认为这条最该看)

InfluxDB 的查询语言在几年里换了两轮方向:

复制代码
1.x  →  InfluxQL(类 SQL)
2.x  →  Flux(全新函数式语言,被推为旗舰)
3.x  →  回到 SQL + InfluxQL,Flux 被弃用

3.x 是 Rust 重写、基于 Apache Arrow / DataFusion,技术上是次大升级;但对在 2.x 时代照官方指引把查询和告警都写成 Flux 的团队来说,等于主力查询语言被废弃了一次------官方甚至提供了「Flux 转 SQL」的转换工具,这本身就说明迁移量是真实存在的。

对比一下:PromQL 从 Prometheus 1.x 到 3.x 一直是 PromQL。 长期存储这种东西一旦上线就要活好几年,语言层的稳定性不是锦上添花。

④ 开源版是单机的,横向扩展要商业授权

  • InfluxDB 3 Core :开源(MIT / Apache 2),但单节点,官方自己的定位是「非生产、边缘、单节点部署」
  • 横向扩展 / 集群 :走 Enterprise,按 CPU 核数授权

如果你换外部存储的动机里包含「单机扛不住了」,那开源版正好不解决这个问题。而 VictoriaMetrics 的集群版本身就是开源的(Apache 2.0,企业版另加降采样等增强),Thanos / Mimir 也都是 Apache 2.0 的完整分布式方案。

⑤ 顺带一个实操摩擦 :1.x 时代有原生的 Prometheus remote write / read 端点,是最干净的接法;到了新版本,常见路径变成 Prometheus → Telegraf(prometheus_remote_write 输入)→ InfluxDB,多一跳组件。这削掉了「用它更简单」的那部分理由。(具体接法各版本不同,动手前查当版文档。)

那什么时候 InfluxDB 才是对的?

当「Prometheus 长期存储」不是你的主要需求时------这时它的优势才用得上:

  • ✅ 存的不只是指标:IoT / 传感器数据、事件流、带字符串和高精度整型的业务时序(Prometheus 模型只有 float64,装不了这些)
  • ✅ 团队要用 SQL(3.x 的 SQL + Arrow 生态是真优势,接 BI 工具比 PromQL 顺)
  • 公司已经在跑 InfluxDB、有现成运维经验和授权------这时多一套 VictoriaMetrics 的运维成本,可能反而大于看板重写的成本
  • ✅ 业务侧时序分析和监控指标想合到一个库里

💡 一句话:InfluxDB 是个通用时序数据库,VictoriaMetrics / Thanos / Mimir 是 Prometheus 生态的原生扩展。 干「给 Prometheus 当长期存储」这份活,生态契合度比数据库本身的素质更决定成败------因为决定你迁移成本的,是那几百个已经写好的 PromQL 查询,不是写入吞吐的 benchmark 数字。

同理适用于 TimescaleDB(PostgreSQL 扩展,SQL 生态强,但同样不是 PromQL)和 GreptimeDB(较新,兼容性和成熟度请自行验证)。


六、总结

回到开头那两个问题:

1. TSDB 能独立部署维护吗?------不能。

  • 它是编译进 Prometheus 的 Go 库,没有独立进程、没有网络协议
  • data 目录有 lock 文件独占,两个进程共享直接起不来
  • 官方明确 不集群、不副本、不支持 NFS/EFS(会不可恢复损坏)
  • 但存储的去向能换remote_write 接外部时序库;要彻底只留采集,用 Agent 模式--agent

2. 存更多数据有限制吗?------有三条,性质不同。

  • 磁盘 :受单机盘限制。磁盘 ≈ 保留秒数 × samples/s × 1~2 字节,再 ×1.2
  • 内存 :跟活跃 series 线性相关,跟保留时长无关。缩 retention 省不了内存
  • 查询 :原生没有降采样,长周期查询必然慢------这条加机器也治不了

排查与决策顺序(照这个顺序走,能省掉大部分无谓的架构升级)

步骤 动作 命令 / 查询
1️⃣ 先排除基数爆炸 Web UI → Status → /tsdb-status
2️⃣ 量自己的真实数字,别套别人的 prometheus_tsdb_head_seriesrate(prometheus_tsdb_head_samples_appended_total[1h])。⚠️ 查出来是空的先看 §3.3 那段「查不到怎么办」------这组自监控指标跟版本、有没有抓自己都有关
3️⃣ 配好两个 retention 阀门 --storage.tsdb.retention.time + .size(给盘的 70~80%)
4️⃣ ≤30 天 → 到此为止,加盘就行 别上 Thanos
5️⃣ 要长存 → remote_write 接外部库 中小团队优先 VictoriaMetrics;有对象存储 + 多套 Prometheus 用 Thanos
6️⃣ 接完必须做三件事 本地 retention ≥ 最长规则窗口×2(别让告警静默失效 );配 samples_dropped_total 告警;Grafana 直连外部库、别用 remote_read

三条最容易踩的(重复一遍,因为代价都很大):

  1. remote_write 不省本地盘------本地照常写,要省得自己调 retention
  2. 远端挂超 2 小时的数据永久丢失 ------WAL 只留 2 小时,samples_dropped_total 必须配 critical 告警
  3. 告警仍在本地评估 ------本地 retention 压太短会让告警静默失效,不报错、不告警,出事那天才发现

一句话收尾:Prometheus 从来没打算做你的长期数据仓库,它只想做一个可靠的采集器和告警引擎。 认清这个定位,就不会指望把 TSDB 拆出来单独维护,而是让它做好短期缓冲,把「存一年」这件事交给专门干这个的系统。


延伸阅读

本站相关

官方文档


📅 最后更新 :2026-08-26。订正 §1.1 对 --storage.tsdb.no-lockfile 的归因 :原文说「它的用途是配合 Agent 模式」------查 cmd/prometheus/main.go 后确认并非如此。源码里其实是两个平行的参数--storage.tsdb.no-lockfile 注册为 serverOnlyFlag(只在普通 server 模式可用) ,而 Agent 模式用的是另一个 --storage.agent.no-lockfileagentOnlyFlag),用错模式直接不认核心警告不变(别拿它去绕开单写限制------锁没了,但两个进程同时写 WAL、同时 compaction 的问题一个没解决),但理由改成了准确的:它是给「部署环境本身已保证单实例」的场景用的逃生舱。已补两者的对照表。


🏷️ 标签Prometheus 时序数据库 remote_write VictoriaMetrics Thanos 监控

相关推荐
李白客1 小时前
时序数据库入门:什么是 TSDB?时序库与关系型数据库对比、主流产品选型指南(2026)
数据库·oracle·时序数据库
Ningcode_cloud4 小时前
Kubernetes 弹性伸缩实验手册:从集群搭建到 HPA / VPA 自动扩缩容
云原生·k8s·prometheus
DolphinDB6 小时前
Text-to-SQL 已过时?DolphinX 正在重新定义 AI 问数
ai·时序数据库·dolphindb
涛思数据(TDengine)8 小时前
存储成本降低80%,Zendure用TDengine支撑117万台设备的能源数据分析
大数据·数据库·人工智能·数据分析·时序数据库·tdengine·工业
2601_962097361 天前
可观测性实战:Prometheus+Grafana+OTel构建监控
grafana·prometheus·微服务架构·可观测性·opentelemetry
这个DBA有点耶1 天前
时间序列数据库选型2026:5款主流产品深度对比与场景适配
大数据·数据库·程序人生·架构·时序数据库·dba·数据库管理员
TDengine (老段)1 天前
TDgpt 概览 — AI 增强的时序数据库
大数据·数据库·人工智能·物联网·时序数据库·iot·tdengine
IT界的老黄牛2 天前
Jenkins 监控一条龙:接入、看板 9964、11 条告警规则直接抄走
jenkins·grafana·prometheus·监控·ci-cd·告警规则
SkyWalking中文站2 天前
SkyWalking 11 与 BanyanDB 0.11:在存储引擎内部实现 Trace 尾部采样
运维·监控·自动化运维