<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Long-Term-Storage on Tarragon</title><link>https://tarrragon.github.io/blog/tags/long-term-storage/</link><description>Recent content in Long-Term-Storage on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 22 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/long-term-storage/index.xml" rel="self" type="application/rss+xml"/><item><title>Remote Write 與長期儲存整合</title><link>https://tarrragon.github.io/blog/backend/04-observability/vendors/prometheus/remote-write-long-term-storage/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/04-observability/vendors/prometheus/remote-write-long-term-storage/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus&lt;/a> 的 vendor deep article，深化 overview「Remote write / read」段。初次接觸 Prometheus 的讀者建議先讀 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus 服務頁&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>Remote write 把 Prometheus 的 metrics 即時推送到外部長期儲存，解決單機 retention 上限與跨實例統一查詢的限制。三個觸發點會讓團隊需要 remote write 與長期儲存：&lt;/p>
&lt;p>Prometheus 預設 retention 是 15 天。業務需要回顧 90 天的趨勢（容量規劃、季度 SLO 報告、成本歸因），本地 disk 不夠放。加大 disk 可以延長 retention，但 Prometheus 的查詢效能會隨資料量下降 — 本地 TSDB 不做 downsampling，查 90 天 range 的 query 要掃描全量 sample。&lt;/p>
&lt;p>多個 Prometheus 實例分散在不同叢集（prod-us、prod-eu、staging），團隊需要一個統一查詢入口看跨叢集 metrics。每個 Prometheus 各自保存自己的資料，沒有跨實例查詢能力。手動切換 Grafana datasource 容易遺漏某個叢集的異常。&lt;/p>
&lt;p>單機 Prometheus 是 SPOF — process crash 或 VM 故障時 metrics 完全不可用。跑兩個 Prometheus 各自 scrape 同一組 target 可以達到 HA，但兩份資料有微小差異（scrape 時間偏移），下游查詢需要 dedup。&lt;/p>
&lt;p>Remote write 解決這三個問題：Prometheus 保持短期本地儲存（scrape + 即時查詢），同時把 metrics 串流到長期儲存後端。長期後端負責壓縮、downsampling、跨實例查詢與 HA dedup。&lt;/p>
&lt;h2 id="核心概念">核心概念&lt;/h2>
&lt;h3 id="remote-write-protocol">Remote write protocol&lt;/h3>
&lt;p>Prometheus 透過 HTTP POST 把 time series 送到 remote write endpoint。每次 POST 包含一批 samples（protobuf 編碼、snappy 壓縮），由 Prometheus 的 WAL（write-ahead log）驅動 — WAL 記錄所有 scrape 到的 samples，remote write 從 WAL 讀取並串流到遠端。&lt;/p>
&lt;p>這個設計意味著 remote write 是 best-effort 但有 buffer：如果遠端暫時不可達，samples 會堆在 WAL 裡等重試。WAL 的大小有上限（&lt;code>--storage.tsdb.wal-segment-size&lt;/code>，預設 128 MB per segment），堆積太多會導致 WAL 佔用大量 disk。&lt;/p>
&lt;h3 id="exemplar-forwarding">Exemplar forwarding&lt;/h3>
&lt;p>Prometheus 2.26 開始支援 exemplar — 在 histogram 或 counter sample 上附加 trace_id / span_id。Remote write 也能把 exemplar 送到支援的後端（Mimir、Grafana Cloud、Tempo）。Exemplar 讓讀者從 metric anomaly 一鍵跳到對應的 trace，是 metrics-to-traces 橋接的關鍵能力。&lt;/p>
&lt;p>啟用方式：scrape config 加 &lt;code>enable_features: [exemplar-storage]&lt;/code>，remote write endpoint 支援 exemplar 即可自動 forward。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus</a> 的 vendor deep article，深化 overview「Remote write / read」段。初次接觸 Prometheus 的讀者建議先讀 <a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus 服務頁</a>。</p></blockquote>
<h2 id="問題情境">問題情境</h2>
<p>Remote write 把 Prometheus 的 metrics 即時推送到外部長期儲存，解決單機 retention 上限與跨實例統一查詢的限制。三個觸發點會讓團隊需要 remote write 與長期儲存：</p>
<p>Prometheus 預設 retention 是 15 天。業務需要回顧 90 天的趨勢（容量規劃、季度 SLO 報告、成本歸因），本地 disk 不夠放。加大 disk 可以延長 retention，但 Prometheus 的查詢效能會隨資料量下降 — 本地 TSDB 不做 downsampling，查 90 天 range 的 query 要掃描全量 sample。</p>
<p>多個 Prometheus 實例分散在不同叢集（prod-us、prod-eu、staging），團隊需要一個統一查詢入口看跨叢集 metrics。每個 Prometheus 各自保存自己的資料，沒有跨實例查詢能力。手動切換 Grafana datasource 容易遺漏某個叢集的異常。</p>
<p>單機 Prometheus 是 SPOF — process crash 或 VM 故障時 metrics 完全不可用。跑兩個 Prometheus 各自 scrape 同一組 target 可以達到 HA，但兩份資料有微小差異（scrape 時間偏移），下游查詢需要 dedup。</p>
<p>Remote write 解決這三個問題：Prometheus 保持短期本地儲存（scrape + 即時查詢），同時把 metrics 串流到長期儲存後端。長期後端負責壓縮、downsampling、跨實例查詢與 HA dedup。</p>
<h2 id="核心概念">核心概念</h2>
<h3 id="remote-write-protocol">Remote write protocol</h3>
<p>Prometheus 透過 HTTP POST 把 time series 送到 remote write endpoint。每次 POST 包含一批 samples（protobuf 編碼、snappy 壓縮），由 Prometheus 的 WAL（write-ahead log）驅動 — WAL 記錄所有 scrape 到的 samples，remote write 從 WAL 讀取並串流到遠端。</p>
<p>這個設計意味著 remote write 是 best-effort 但有 buffer：如果遠端暫時不可達，samples 會堆在 WAL 裡等重試。WAL 的大小有上限（<code>--storage.tsdb.wal-segment-size</code>，預設 128 MB per segment），堆積太多會導致 WAL 佔用大量 disk。</p>
<h3 id="exemplar-forwarding">Exemplar forwarding</h3>
<p>Prometheus 2.26 開始支援 exemplar — 在 histogram 或 counter sample 上附加 trace_id / span_id。Remote write 也能把 exemplar 送到支援的後端（Mimir、Grafana Cloud、Tempo）。Exemplar 讓讀者從 metric anomaly 一鍵跳到對應的 trace，是 metrics-to-traces 橋接的關鍵能力。</p>
<p>啟用方式：scrape config 加 <code>enable_features: [exemplar-storage]</code>，remote write endpoint 支援 exemplar 即可自動 forward。</p>
<h3 id="dedup-策略">Dedup 策略</h3>
<p>跑兩個 Prometheus HA pair 時，兩個實例都 scrape 同一組 target、都 remote write 到同一個後端。後端會收到兩份幾乎相同但不完全一致的 samples（scrape 時間差 ±1-2 秒）。</p>
<p>Thanos 和 Mimir 都有 dedup 機制：Thanos 在 query 層根據 <code>external_labels</code>（replica label）做 dedup，每個 time window 只取一個 replica 的值。Mimir 在 ingester 層做 dedup，同一個 series 的重複 sample 在寫入時合併。</p>
<p>Dedup 的前提是兩個 Prometheus 實例設定不同的 <code>external_labels</code>（例如 <code>replica: a</code> / <code>replica: b</code>），讓後端能辨別哪些 series 是同一組的不同副本。</p>
<h2 id="配置">配置</h2>
<h3 id="remote-write-基本設定">Remote write 基本設定</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># prometheus.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">remote_write</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span>- <span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://mimir-distributor:9009/api/v1/push&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="nt">queue_config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">      </span><span class="nt">capacity</span><span class="p">:</span><span class="w"> </span><span class="m">10000</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">      </span><span class="nt">max_shards</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">      </span><span class="nt">max_samples_per_send</span><span class="p">:</span><span class="w"> </span><span class="m">5000</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span><span class="nt">batch_send_deadline</span><span class="p">:</span><span class="w"> </span><span class="l">5s</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nt">write_relabel_configs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">      </span>- <span class="nt">source_labels</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">__name__]</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="nt">regex</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;go_.*&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">        </span><span class="nt">action</span><span class="p">:</span><span class="w"> </span><span class="l">drop</span></span></span></code></pre></div><p><code>queue_config</code> 控制 remote write 的並行度與批次大小：</p>
<ul>
<li><code>capacity</code>：內存中暫存的 sample 數量。太小會頻繁 flush、太大會佔記憶體</li>
<li><code>max_shards</code>：並行的 write goroutine 數量。Shard 太少會造成 backlog、太多會壓垮遠端</li>
<li><code>max_samples_per_send</code>：每次 POST 的 sample 數量。5000 是常用值</li>
<li><code>batch_send_deadline</code>：即使 batch 沒滿也在這個時間內 flush，避免低流量時 sample 延遲太久</li>
</ul>
<p><code>write_relabel_configs</code> 在 remote write 前過濾 series — 不需要長期保存的 internal metrics（go runtime、scrape metadata）可以在這裡 drop，減少長期儲存的 cardinality 與成本。</p>
<h3 id="external-labelsha-與多叢集">External labels（HA 與多叢集）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">global</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">external_labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">cluster</span><span class="p">:</span><span class="w"> </span><span class="l">prod-us</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="nt">replica</span><span class="p">:</span><span class="w"> </span><span class="l">a</span></span></span></code></pre></div><p><code>cluster</code> label 區分來源叢集，<code>replica</code> label 讓長期儲存做 dedup。每個 Prometheus 實例的 external_labels 必須唯一。</p>
<h3 id="三家長期儲存比較">三家長期儲存比較</h3>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Mimir</th>
          <th>Thanos</th>
          <th>Cortex</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>架構模式</td>
          <td>Microservice（distributor / ingester / compactor / querier）</td>
          <td>Sidecar + Store Gateway + Compactor + Query</td>
          <td>Microservice（跟 Mimir 同源、Mimir 是 Cortex fork）</td>
      </tr>
      <tr>
          <td>部署複雜度</td>
          <td>中（Helm chart，最少 4 個元件）</td>
          <td>中高（sidecar 綁 Prometheus pod，元件分散）</td>
          <td>高（元件多、已進入維護模式）</td>
      </tr>
      <tr>
          <td>Query layer</td>
          <td>原生 PromQL + split/merge</td>
          <td>Thanos Query 做 fan-out + dedup</td>
          <td>原生 PromQL（跟 Mimir 共用）</td>
      </tr>
      <tr>
          <td>多租戶</td>
          <td>原生（X-Scope-OrgID header）</td>
          <td>有限（靠 label 或獨立部署）</td>
          <td>原生（Mimir 繼承）</td>
      </tr>
      <tr>
          <td>Downsampling</td>
          <td>支援（compactor 做 1h/5m 降取樣）</td>
          <td>支援（compactor）</td>
          <td>支援</td>
      </tr>
      <tr>
          <td>開發狀態</td>
          <td>活躍（Grafana Labs 主推）</td>
          <td>活躍（CNCF incubating）</td>
          <td>維護模式（Grafana Labs 把精力轉到 Mimir）</td>
      </tr>
      <tr>
          <td>對象儲存</td>
          <td>S3 / GCS / Azure Blob</td>
          <td>S3 / GCS / Azure Blob / 本地</td>
          <td>S3 / GCS</td>
      </tr>
      <tr>
          <td>成本模型</td>
          <td>自管 compute + storage；Grafana Cloud 按 active series 計費</td>
          <td>自管 compute + storage</td>
          <td>自管（不推薦新部署）</td>
      </tr>
  </tbody>
</table>
<p>選擇判準依三個維度排序：</p>
<p><strong>已經在用 Grafana 生態</strong>（Grafana dashboard、Loki、Tempo）：Mimir 是最自然的選擇，跟 Grafana Stack 的整合最深，Grafana Cloud 可以免管 Mimir。</p>
<p><strong>需要最小化對 Prometheus 的改動</strong>：Thanos sidecar 模式不改 Prometheus 配置（sidecar 讀本地 TSDB block），適合「先加長期儲存、Prometheus 維持現狀」的漸進路徑。但 sidecar 綁 Prometheus pod，K8s 環境外的部署更複雜。</p>
<p><strong>多租戶需求</strong>：Mimir 原生支援多租戶隔離（每個 tenant 獨立 TSDB、query isolation），Thanos 的多租戶靠 label 或獨立部署。</p>
<p>Cortex 是 Mimir 的前身，新部署不推薦。既有 Cortex 部署可參考 Grafana Labs 的 Mimir migration guide。</p>
<h3 id="uber-m3-的第四條路">Uber M3 的第四條路</h3>
<p><a href="/blog/backend/04-observability/cases/uber-m3-metrics-platform-scale/" data-link-title="4.C11 Uber：M3 大規模 Metrics 平台" data-link-desc="從散落的 Prometheus 實例到統一 metrics 平台，處理 cardinality 爆炸、長期 retention 與跨叢集查詢的規模化挑戰。">Uber M3 案例</a>選擇了自建 M3DB 而非 Mimir / Thanos / Cortex — 原因是 M3DB 在 2018 年啟動時、Mimir 尚未存在、Cortex 還在早期階段、Thanos 也剛開源。M3DB 的設計核心是 namespace-level retention（不同 namespace 不同 retention 跟 resolution）、跟 Uber 的 etcd service discovery 深度整合。</p>
<p>M3 的經驗對後來的三家有直接影響：Mimir 的 per-tenant retention、Thanos 的 downsampling compactor、都能追溯到 M3 先踩過的問題。今天做新部署不需要重走 M3 的路 — Mimir 跟 Thanos 已經成熟。但 M3 案例揭露的設計判準仍然有效：</p>
<ul>
<li><strong>跨 cluster 查詢需要 fan-out + dedup</strong>：三家都實作了這個能力，但部署配置跟 dedup 策略各有差異</li>
<li><strong>Downsampling 是長期成本控制的必要手段</strong>：不做 downsampling、90 天 range query 的效能跟成本都不可接受</li>
<li><strong>多租戶隔離不只是 query 層面</strong>：ingestion rate limit 跟 storage quota per tenant 才能防止「一個團隊的 cardinality 爆炸拖垮整個平台」</li>
</ul>
<h2 id="故障與邊界">故障與邊界</h2>
<h3 id="remote-write-backlog-佔滿-wal">Remote write backlog 佔滿 WAL</h3>
<p><strong>觸發條件</strong>：遠端不可達（network 問題、後端過載）持續超過數分鐘，WAL segment 堆積。</p>
<p><strong>表現</strong>：<code>prometheus_remote_storage_bytes_total</code> 停止增長（寫不出去）、<code>prometheus_wal_storage_size_bytes</code> 持續增長、disk 使用率上升。嚴重時 WAL 佔滿 disk，Prometheus 無法寫入新 sample、連 local scrape 也受影響。</p>
<p><strong>修復</strong>：先恢復遠端連線。WAL backlog 會在連線恢復後自動 catch up — Prometheus 按 WAL 順序重送積壓的 samples。如果 catch up 時間太長（例如堆了數小時），remote write 的 max_shards 可以暫時調高加速回補，但要注意不要壓垮剛恢復的遠端。</p>
<p><strong>預防</strong>：監控 <code>prometheus_remote_storage_queue_highest_sent_timestamp_seconds</code> 跟 current time 的差距 — 差距代表 remote write 延遲。差距超過 5 分鐘時告警。設定 WAL 的 disk 空間上限（<code>--storage.tsdb.max-block-duration</code> 搭配 retention 控制 total disk）。</p>
<h3 id="target-不可達時的-retry-storm">Target 不可達時的 retry storm</h3>
<p><strong>觸發條件</strong>：remote write endpoint 回傳 5xx 或 429（rate limit），Prometheus 進入指數退避重試。大量 shard 同時 retry，CPU 跟 network 消耗上升。</p>
<p><strong>表現</strong>：<code>prometheus_remote_storage_retried_samples_total</code> 增長、CPU 使用上升、remote write 延遲拉大。如果後端本來就過載，retry storm 會讓情況惡化。</p>
<p><strong>修復</strong>：remote write 配置中的 <code>min_backoff</code> / <code>max_backoff</code> 控制 retry 間隔（預設 30ms / 5s）。可以調高 <code>min_backoff</code> 減緩 retry 頻率。長期修法是讓後端回傳 429 搭配 <code>Retry-After</code> header，Prometheus 會遵守。</p>
<h3 id="metrics-語意-drift">Metrics 語意 drift</h3>
<p><strong>觸發條件</strong>：多個 Prometheus 實例的 <code>write_relabel_configs</code> 不一致、或 external_labels 設定有誤。</p>
<p><strong>表現</strong>：同一個 metric 在長期儲存中出現語意不同的 series — 有些 instance 保留了某個 label、有些 drop 掉了。Dashboard 查詢結果不一致（取決於查到哪個實例的 series）。</p>
<p><strong>修復</strong>：remote write 的 <code>write_relabel_configs</code> 集中管理（配置模板或 Prometheus Operator 的 PrometheusSpec.remoteWrite）。每次修改 relabel 規則後，驗證所有實例的 series label set 一致。Mimir 的 <code>active_series</code> API 可以列出目前所有 active series 的 label set。</p>
<h3 id="remote-write-protocol-版本不匹配">Remote write protocol 版本不匹配</h3>
<p><strong>觸發條件</strong>：Prometheus 版本跟長期儲存後端期望的 remote write protocol 版本不一致。Prometheus 2.x 使用 remote write v1（protobuf + snappy），部分較新後端開始支援 v2（native histogram 支援、metadata 改進）。</p>
<p><strong>表現</strong>：後端回傳 400 Bad Request。Prometheus 對 4xx 的預設行為是不 retry（視為 client error、retry 無意義），samples 被 drop。<code>prometheus_remote_storage_samples_failed_total</code> 增長但不像 5xx 那樣有明顯的 retry storm — 靜默丟失更難察覺。</p>
<p><strong>修復</strong>：確認 Prometheus 版本跟後端的 protocol 相容性。Mimir / Thanos 的文件通常標明支援的 remote write protocol 版本。版本不匹配時升級 Prometheus 或降級後端配置。</p>
<h3 id="何時單機-prometheus-不夠">何時單機 Prometheus 不夠</h3>
<p>三個訊號同時出現時，remote write + 長期儲存從「可選」變成「必要」：</p>
<p><strong>Active series 超過 500 萬</strong>。單機 Prometheus 在 500 萬 series 左右開始出現記憶體壓力（head block ~20 GB）、WAL replay 時間拉長（重啟要數分鐘）、compaction 佔用 CPU。<a href="/blog/backend/04-observability/cases/uber-m3-metrics-platform-scale/" data-link-title="4.C11 Uber：M3 大規模 Metrics 平台" data-link-desc="從散落的 Prometheus 實例到統一 metrics 平台，處理 cardinality 爆炸、長期 retention 與跨叢集查詢的規模化挑戰。">Uber 在 M3 專案</a>遇到的正是這個天花板 — 數十個叢集各自 scrape 的 metrics 匯總後 series 數遠超單機能力，但「用更大的 VM 跑 Prometheus」不是解法，因為 Prometheus 的 TSDB 是單線程 compaction、垂直擴展的效益有上限。</p>
<p><strong>Retention 需求超過 30 天</strong>。本地 TSDB 的 retention 拉長時，range query 的效能線性退化 — 查 90 天 range 要掃描的 block 數量是 15 天的 6 倍。Downsampling 是長期儲存後端的標準能力（Mimir / Thanos compactor 把 5 分鐘 resolution 降到 1 小時），但 Prometheus 本地 TSDB 不做 downsampling。Uber 的 M3DB 設計了 namespace-level retention（short-term 48h full resolution、long-term 1y downsampled），讓查詢成本不隨 retention 線性成長。</p>
<p><strong>跨叢集統一查詢</strong>。多個 Prometheus 各自 scrape 不同 cluster 時，工程師需要一個入口看「所有 cluster 的 checkout error rate」。手動切 Grafana datasource 容易遺漏。Remote write 把所有 Prometheus 的 metrics 匯入同一個長期儲存、用單一查詢入口（Mimir querier / Thanos Query）做 fan-out。</p>
<p>這三個需求在中型公司（50-200 服務、3+ K8s cluster）通常在 1-2 年內同時浮現。規劃 remote write 時不用等三個都出現 — 任一個出現就是啟動的合理時機。</p>
<h2 id="容量與-cost">容量與 Cost</h2>
<h3 id="remote-write-bandwidth">Remote write bandwidth</h3>
<p>Remote write 的 bandwidth ≈ ingestion rate × 每 sample 壓縮後大小（約 1-2 bytes with snappy）。</p>
<table>
  <thead>
      <tr>
          <th>Ingestion rate</th>
          <th>估算 bandwidth</th>
          <th>對應規模參考</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>10 萬 samples/sec</td>
          <td>~100-200 KB/s</td>
          <td>小型：5-10 服務、1 cluster</td>
      </tr>
      <tr>
          <td>50 萬 samples/sec</td>
          <td>~500 KB/s-1 MB/s</td>
          <td>中型：50 服務、2-3 cluster</td>
      </tr>
      <tr>
          <td>200 萬 samples/sec</td>
          <td>~2-4 MB/s</td>
          <td>大型：200 服務、5+ cluster</td>
      </tr>
      <tr>
          <td>1000 萬 samples/sec</td>
          <td>~10-20 MB/s</td>
          <td>平台級：Uber M3 等級</td>
      </tr>
  </tbody>
</table>
<p>每個 active series 在 15 秒 scrape interval 下每秒產生 ~0.067 個 sample。100 萬 active series 的 ingestion rate ≈ 6.7 萬 samples/sec，對應 ~70-140 KB/s remote write bandwidth。這個數字在內網環境下通常不是瓶頸。</p>
<p>真正的瓶頸在兩個地方：<strong>roundtrip latency</strong> 決定單 shard 吞吐上限（每次 POST 等回應才發下一批）、<strong>後端 ingestion capacity</strong> 決定能消化多少 samples/sec。Mimir 的 distributor 跟 ingester 可以水平擴展，但每加一個 ingester 增加 compute 成本。bandwidth 只是 capacity planning 的第一步，實際規模要用 Mimir 的 <code>cortex_distributor_received_samples_total</code> 跟 <code>cortex_ingester_memory_series</code> 做持續觀測。</p>
<h3 id="長期儲存的-compaction-與-downsampling-cost">長期儲存的 compaction 與 downsampling cost</h3>
<p>Mimir 和 Thanos 的 compactor 定期合併 block 並做 downsampling（5m → 1h 粒度）。Compaction 消耗 CPU 和 disk I/O，但跑在長期儲存自己的 compute 上，不影響 Prometheus。</p>
<p>成本結構：</p>
<ul>
<li><strong>Compute</strong>：distributor + ingester + querier + compactor 的 CPU / memory。Mimir 官方建議 ingester 是最吃資源的元件（記憶體中保存 active series）</li>
<li><strong>Object storage</strong>：S3 / GCS 的儲存量 ≈ ingestion rate × retention × 壓縮率。Compaction 跟 downsampling 會降低儲存量（通常 2-5x 壓縮）</li>
<li><strong>Query cost</strong>：長 range query 需要讀大量 block — 在 cloud object storage 上是 GET request 成本。Mimir 用 index cache（memcached）降低重複查詢的 GET request</li>
</ul>
<p>跟 Prometheus 本地 TSDB 比，長期儲存把 disk cost 換成 object storage cost（通常更便宜），但增加了 compute cost（長期儲存的 ingester / querier / compactor）。判斷轉折點的方式是比較本地 SSD cost × retention 跟 object storage cost + compute cost。retention 超過 30 天時，object storage 的成本優勢通常明顯。</p>
<h2 id="整合與下一步">整合與下一步</h2>
<h3 id="接-grafana-stack-lgtm">接 Grafana Stack LGTM</h3>
<p>Mimir 是 <a href="/blog/backend/04-observability/vendors/grafana-stack/" data-link-title="Grafana Stack" data-link-desc="Grafana / Loki / Tempo / Mimir / Pyroscope 全棧">Grafana Stack</a> LGTM（Loki + Grafana + Tempo + Mimir）的 metrics 後端。Prometheus remote write 到 Mimir 後，Grafana 用 Mimir 作為 Prometheus-compatible datasource，查詢語言仍是 PromQL。Exemplar forwarding 讓 Mimir metrics 可以連結到 Tempo traces。</p>
<h3 id="接-telemetry-pipeline">接 Telemetry Pipeline</h3>
<p>Remote write 在 <a href="/blog/backend/04-observability/telemetry-pipeline/" data-link-title="4.11 Telemetry Pipeline 架構" data-link-desc="把 log / metric / trace 的 agent → collector → ingest → storage → query 分層治理">4.11 telemetry pipeline</a> 中扮演 metrics ingestion 段。如果同時使用 OpenTelemetry Collector，Collector 可以作為 remote write 的中繼（接收 Prometheus scrape → OTLP export → Mimir OTLP endpoint），但多一層中繼增加了 failure point。直接 Prometheus → Mimir remote write 是最簡路徑。</p>
<h3 id="接-cost-attribution">接 Cost Attribution</h3>
<p>長期儲存的多租戶能力讓 <a href="/blog/backend/04-observability/cost-attribution/" data-link-title="4.15 Cost Attribution / Chargeback" data-link-desc="把 observability 成本拆到團隊、產品、環境維度">4.15 cost attribution</a> 可以按 tenant / team / service 拆分 metrics 成本。Mimir 的 per-tenant active series quota 同時控制 cardinality 與成本。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus 服務頁</a>：overview 跟日常操作入口</li>
<li><a href="../promql-recording-rules/">PromQL 與 Recording Rules 實務</a>：remote write 架構下 recording rules 的部署位置選擇</li>
<li><a href="../capacity-failure-modes/">容量規劃與故障模式</a>：remote write 作為容量超限時的卸載路徑</li>
<li><a href="/blog/backend/04-observability/vendors/grafana-stack/" data-link-title="Grafana Stack" data-link-desc="Grafana / Loki / Tempo / Mimir / Pyroscope 全棧">Grafana Stack</a>：Mimir 作為長期儲存的完整操作指南</li>
<li><a href="/blog/backend/04-observability/telemetry-pipeline/" data-link-title="4.11 Telemetry Pipeline 架構" data-link-desc="把 log / metric / trace 的 agent → collector → ingest → storage → query 分層治理">4.11 Telemetry Pipeline</a>：remote write 在 pipeline 架構中的定位</li>
<li><a href="/blog/backend/04-observability/cost-attribution/" data-link-title="4.15 Cost Attribution / Chargeback" data-link-desc="把 observability 成本拆到團隊、產品、環境維度">4.15 Cost Attribution</a>：多租戶 metrics 的成本拆分</li>
</ul>
]]></content:encoded></item></channel></rss>