<?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>Consensus on Tarragon</title><link>https://tarrragon.github.io/blog/tags/consensus/</link><description>Recent content in Consensus on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 27 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/consensus/index.xml" rel="self" type="application/rss+xml"/><item><title>CockroachDB HLC + Raft Consensus：軟體時鐘 + per-range 共識的 latency 與容量結構</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/cockroachdb/hlc-raft-consensus/</link><pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/cockroachdb/hlc-raft-consensus/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/cockroachdb/" data-link-title="CockroachDB" data-link-desc="分散式 SQL、PostgreSQL 相容、跨區強一致、Spanner 的開源 / 跨雲替代">CockroachDB vendor overview&lt;/a> 的 implementation-layer deep article。Overview 已界定 CockroachDB 在 distributed SQL 譜系的定位、本文聚焦 &lt;em>HLC + Raft + range + leaseholder 四層機制&lt;/em> — 解釋為什麼 distributed SQL 的 latency / 容量曲線跟 PostgreSQL single-primary 完全不同、以及怎麼從 production 訊號倒推它對團隊的成本結構。寫作參照 &lt;a href="https://tarrragon.github.io/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">vendor deep article methodology&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;h2 id="為什麼這篇先講-hlc--raft">為什麼這篇先講 HLC + Raft&lt;/h2>
&lt;p>團隊評估 CockroachDB 替代 PostgreSQL streaming replication 時、會同時看到兩個訊號：「跨 region 強一致」很吸引人、「每次寫都經過 Raft majority」又讓人害怕。前者是賣點、後者是成本結構 — 不先把 HLC / Raft / range / leaseholder 拆清楚、後面講 survival goal、locality、transaction retry 都會卡在「為什麼這個機制存在」這層。&lt;/p>
&lt;p>讀者最常問的三題：&lt;/p>
&lt;ul>
&lt;li>Spanner 用 TrueTime 原子鐘做線性化、CockroachDB 沒硬體時鐘怎麼保證 ordering？&lt;/li>
&lt;li>Raft 每次寫要等 majority ack、不是比 PostgreSQL 慢得多？&lt;/li>
&lt;li>HLC clock skew 超出容忍區間時會發生什麼？節點隨機 panic 嗎？&lt;/li>
&lt;/ul>
&lt;p>三題都不只是 spec 問題、而是 &lt;em>production 容量規劃跟 incident 訊號的根本前置&lt;/em>。&lt;/p>
&lt;p>問題情境最常見的 trigger：&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/doordash-cockroachdb-orders-platform/" data-link-title="9.C39 DoorDash：Aurora Postgres 寫入瓶頸 → CockroachDB 多主寫入" data-link-desc="DoorDash 從 Aurora Postgres 遷到 CockroachDB、解 1.6 M QPS 單主寫入瓶頸、外送平台爆量壓力下重做 OLTP 拓樸">9.C39 DoorDash&lt;/a> 在 2020-04-17 高峰 Aurora Postgres 撞到 1.636 M QPS、multi-hour outage。&lt;strong>這個數字是 Aurora 在那個時間點撞牆的痛點、case 自己警示「不是 CockroachDB 撐到 1.636 M QPS 的 throughput claim」&lt;/strong>。case 沒揭露遷移後單一 CockroachDB cluster 的峰值、只說「跑更多 cluster、alert volume 反而下降」。要把 CockroachDB 當寫入容量解法評估、就得先理解 Raft per range 怎麼把寫入從 single-primary 分散到多 node。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/netflix-cockroachdb-multi-region-fleet/" data-link-title="9.C40 Netflix：380&amp;#43; CockroachDB cluster 的 multi-active 拓樸艦隊" data-link-desc="Netflix 把 Cassandra 不夠用的 transactional workload 移到 CockroachDB、380&amp;#43; cluster / 60&amp;#43; 跨 region、含 Open Connect、studio cloud drive、gaming control plane">9.C40 Netflix&lt;/a> 則提供另一條訊號：380+ cluster / 60+ multi-region、最大單區 cluster 60 nodes / 26.5 TB。這個規模證明 Raft 維運在 production 可承擔、但也揭露容量規劃顆粒不是「全公司一條容量曲線」、是「每 cluster 各自規劃」— artery of small DBs。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/01-database/vendors/cockroachdb/" data-link-title="CockroachDB" data-link-desc="分散式 SQL、PostgreSQL 相容、跨區強一致、Spanner 的開源 / 跨雲替代">CockroachDB vendor overview</a> 的 implementation-layer deep article。Overview 已界定 CockroachDB 在 distributed SQL 譜系的定位、本文聚焦 <em>HLC + Raft + range + leaseholder 四層機制</em> — 解釋為什麼 distributed SQL 的 latency / 容量曲線跟 PostgreSQL single-primary 完全不同、以及怎麼從 production 訊號倒推它對團隊的成本結構。寫作參照 <a href="/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">vendor deep article methodology</a>。</p></blockquote>
<hr>
<h2 id="為什麼這篇先講-hlc--raft">為什麼這篇先講 HLC + Raft</h2>
<p>團隊評估 CockroachDB 替代 PostgreSQL streaming replication 時、會同時看到兩個訊號：「跨 region 強一致」很吸引人、「每次寫都經過 Raft majority」又讓人害怕。前者是賣點、後者是成本結構 — 不先把 HLC / Raft / range / leaseholder 拆清楚、後面講 survival goal、locality、transaction retry 都會卡在「為什麼這個機制存在」這層。</p>
<p>讀者最常問的三題：</p>
<ul>
<li>Spanner 用 TrueTime 原子鐘做線性化、CockroachDB 沒硬體時鐘怎麼保證 ordering？</li>
<li>Raft 每次寫要等 majority ack、不是比 PostgreSQL 慢得多？</li>
<li>HLC clock skew 超出容忍區間時會發生什麼？節點隨機 panic 嗎？</li>
</ul>
<p>三題都不只是 spec 問題、而是 <em>production 容量規劃跟 incident 訊號的根本前置</em>。</p>
<p>問題情境最常見的 trigger：<a href="/blog/backend/09-performance-capacity/cases/doordash-cockroachdb-orders-platform/" data-link-title="9.C39 DoorDash：Aurora Postgres 寫入瓶頸 → CockroachDB 多主寫入" data-link-desc="DoorDash 從 Aurora Postgres 遷到 CockroachDB、解 1.6 M QPS 單主寫入瓶頸、外送平台爆量壓力下重做 OLTP 拓樸">9.C39 DoorDash</a> 在 2020-04-17 高峰 Aurora Postgres 撞到 1.636 M QPS、multi-hour outage。<strong>這個數字是 Aurora 在那個時間點撞牆的痛點、case 自己警示「不是 CockroachDB 撐到 1.636 M QPS 的 throughput claim」</strong>。case 沒揭露遷移後單一 CockroachDB cluster 的峰值、只說「跑更多 cluster、alert volume 反而下降」。要把 CockroachDB 當寫入容量解法評估、就得先理解 Raft per range 怎麼把寫入從 single-primary 分散到多 node。</p>
<p><a href="/blog/backend/09-performance-capacity/cases/netflix-cockroachdb-multi-region-fleet/" data-link-title="9.C40 Netflix：380&#43; CockroachDB cluster 的 multi-active 拓樸艦隊" data-link-desc="Netflix 把 Cassandra 不夠用的 transactional workload 移到 CockroachDB、380&#43; cluster / 60&#43; 跨 region、含 Open Connect、studio cloud drive、gaming control plane">9.C40 Netflix</a> 則提供另一條訊號：380+ cluster / 60+ multi-region、最大單區 cluster 60 nodes / 26.5 TB。這個規模證明 Raft 維運在 production 可承擔、但也揭露容量規劃顆粒不是「全公司一條容量曲線」、是「每 cluster 各自規劃」— artery of small DBs。</p>
<h2 id="核心機制hlc--raft--range--leaseholder-四層">核心機制：HLC + Raft + range + leaseholder 四層</h2>
<p>CockroachDB 的線性化保證來自四層機制疊加、缺一層都解釋不通實際 latency / failure 行為。</p>
<h3 id="hlc軟體時鐘把-wall-clock--logical-counter-混在一起">HLC：軟體時鐘把 wall clock + logical counter 混在一起</h3>
<p><a href="/blog/backend/knowledge-cards/hybrid-logical-clock/" data-link-title="Hybrid Logical Clock" data-link-desc="用 physical wall clock &#43; monotonic logical counter 給每個事件 timestamp、靠軟體 max-offset 保證跨節點時鐘差不超過上限、超過 panic 保護一致性">Hybrid Logical Clock</a> 結合 <em>physical time</em>（NTP 同步的牆鐘）跟 <em>logical counter</em>（單調遞增的事件序號）、給每個事件一個 <code>(physical, logical)</code> timestamp。對比 Spanner TrueTime 直接靠 GPS + atomic clock 給「時鐘 uncertainty bound」、CockroachDB HLC 不依賴硬體、用軟體保證「節點之間時鐘最多差 <code>max-offset</code>（default 500ms）、超過就 panic」。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Node A 收到 write at wall=12:00:00.123, last_seen=12:00:00.100
</span></span><span class="line"><span class="ln">2</span><span class="cl">  → HLC = (12:00:00.123, 0)
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">Node A 收到 RPC from B at wall=12:00:00.140, B.HLC=(12:00:00.200, 5)
</span></span><span class="line"><span class="ln">5</span><span class="cl">  → A 跳到 B 的 physical (12:00:00.200)、logical = 6
</span></span><span class="line"><span class="ln">6</span><span class="cl">  → HLC = (12:00:00.200, 6)</span></span></code></pre></div><p>HLC 的契約 <em>只要節點間時鐘差不超過 max-offset、所有 transaction 仍是 linearizable</em>。production 必跑 NTP / chronyd — 一旦本機時鐘飄超過 500ms、節點自動 panic 保護 cluster 一致性、不會發出錯誤 commit。</p>
<p>跟 Spanner TrueTime 對比：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>CockroachDB HLC</th>
          <th>Spanner TrueTime</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>硬體依賴</td>
          <td>無（純軟體 + NTP）</td>
          <td>GPS + atomic clock（每資料中心配）</td>
      </tr>
      <tr>
          <td>Uncertainty</td>
          <td>由 max-offset 上界、固定 500ms</td>
          <td>動態 uncertainty interval（通常 &lt; 7ms）</td>
      </tr>
      <tr>
          <td>Commit 等待</td>
          <td>不需要 wait out uncertainty</td>
          <td>需要 wait out（commit-wait）</td>
      </tr>
      <tr>
          <td>部署彈性</td>
          <td>任何雲 / on-prem 都可跑</td>
          <td>只在有 TrueTime infra 的 GCP region</td>
      </tr>
  </tbody>
</table>
<p>兩條路徑解同一個 <em>event ordering</em> 問題、用不同 trade-off。CockroachDB 把硬體成本換成軟體 max-offset 容忍度、結果是「可以跨雲跨 on-prem 跑、但 NTP 維運是必要條件」。</p>
<h3 id="raft每個-range-一個獨立的-majority-consensus-group">Raft：每個 range 一個獨立的 majority consensus group</h3>
<p>Raft 把寫入流程切成 <em>propose → replicate to majority → commit</em> 三段。每個 range 維護自己的 Raft group、預設 3 replica、寫入要至少 2 個 replica ack 才能 commit。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Client → Leaseholder (Raft leader)
</span></span><span class="line"><span class="ln">2</span><span class="cl">   1. Propose log entry (write intent)
</span></span><span class="line"><span class="ln">3</span><span class="cl">   2. Replicate to 2 follower replicas
</span></span><span class="line"><span class="ln">4</span><span class="cl">   3. Wait for majority ack (本身 + 1 個 follower)
</span></span><span class="line"><span class="ln">5</span><span class="cl">   4. Commit、apply to state machine
</span></span><span class="line"><span class="ln">6</span><span class="cl">   5. Reply to client</span></span></code></pre></div><p>關鍵差異跟 PostgreSQL streaming replication 比：</p>
<ul>
<li>PostgreSQL primary：1 個節點 ack 就 commit（async replication）、replica 可能落後</li>
<li>PostgreSQL sync replication：1 個 standby ack 才 commit、但仍是「primary 是 single point of write」</li>
<li>CockroachDB Raft：majority（2 of 3）ack 才 commit、任何 replica 都可以是 leaseholder、寫入分散到所有節點</li>
</ul>
<p>寫入 latency 因此 <em>結構性</em> 高於 PostgreSQL — 多了一次 cross-node round trip。但寫入 <em>吞吐</em> 可以線性擴展、因為不同 range 的 Raft group 跑在不同節點上。</p>
<h3 id="range把-key-space-切成-512-mb-的可分裂單位">Range：把 key space 切成 ~512 MB 的可分裂單位</h3>
<p>CockroachDB 用 <a href="/blog/backend/knowledge-cards/range-sharding/" data-link-title="Range Sharding" data-link-desc="分散式 SQL 把 key space 切成可自動 split / merge 的 range、每個 range 自己的 consensus group、application 透明">Range Sharding</a> 把整個 key space 切成 range、每個 range 預設上限 ~512 MB、超過自動 split。每個 range 是一個獨立的 Raft group、有自己的 3 replica 分佈。</p>
<p>對比其他 distributed DB 的等價概念：</p>
<ul>
<li>DynamoDB partition：固定 hash 分區、自動 split 但 hot partition 容易撞 ceiling</li>
<li>Spanner split：類似 range、但配置 / placement 語法不同</li>
<li>Vitess keyspace：application 端決定 shard key、不透明 split</li>
</ul>
<p>CockroachDB range 是 <em>系統內建透明</em> 的 — application 只看到 SQL table、不需要 shard key 設計。但 hot range 仍會發生（後面 failure mode 段展開）。</p>
<h3 id="leaseholder每個-range-的-read--write-entry-point">Leaseholder：每個 range 的 read / write entry point</h3>
<p>每個 range 在任一時間點有一個 <a href="/blog/backend/knowledge-cards/leaseholder/" data-link-title="Leaseholder" data-link-desc="分散式 SQL 每個 range 在任一時間點的 read / write entry point、通常等於 Raft leader、承擔該 range 的 coordination">Leaseholder</a>（通常等於 Raft leader）、承擔該 range 的所有 read / write coordination。leaseholder 也是 <em>follower read</em> 的 timestamp 邊界 holder。</p>
<p>leaseholder 概念對 production 訊號的影響：</p>
<ul>
<li>寫入 latency 主要來自 leaseholder → follower replicas 的 Raft round trip</li>
<li>leaseholder 集中在某節點 → 該節點 CPU 飽和（hot range 的根因之一）</li>
<li>leaseholder 換手（lease transfer）短期 p99 spike — rebalance 期間 / 節點 graceful drain 都會觸發</li>
</ul>
<h2 id="操作流程配置--驗證--rollback-邊界">操作流程：配置 + 驗證 + rollback 邊界</h2>
<h3 id="cluster-起手配置">Cluster 起手配置</h3>
<p>最小可運行配置是 3 節點（Raft quorum 下界）、production 通常 9 節點以上（3 region × 3 replica）。每個節點啟動時必須帶 locality tag、讓 Raft placement 知道副本怎麼分佈：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">cockroach start --insecure <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --locality<span class="o">=</span><span class="nv">region</span><span class="o">=</span>us-east1,zone<span class="o">=</span>us-east1-a <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --max-offset<span class="o">=</span>500ms <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>  --join<span class="o">=</span>node1:26257,node2:26257,node3:26257</span></span></code></pre></div><p><code>--max-offset</code> 是 HLC 容忍上界、超過會 panic — 不要為了「避免 panic」加大這個值、會犧牲 linearizability 保證。</p>
<p>NTP / chronyd 是 <em>必要前置</em>、不是 nice-to-have。production 應該在每個節點配置：</p>
<ul>
<li>NTP server 至少 3 個獨立 source（避免單一 server drift）</li>
<li>監控 <code>chronyc tracking</code> 的 offset、超過 100ms 就應該 alert（遠在 500ms panic 邊界之前）</li>
</ul>
<h3 id="驗證點">驗證點</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- 看每節點當前 clock offset 跟 cluster 其他節點
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">node_id</span><span class="p">,</span><span class="w"> </span><span class="n">address</span><span class="p">,</span><span class="w"> </span><span class="n">offset_min_nanos</span><span class="p">,</span><span class="w"> </span><span class="n">offset_max_nanos</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">crdb_internal</span><span class="p">.</span><span class="n">gossip_nodes</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w"></span><span class="c1">-- 看 Raft 健康（每個 range 的 leaseholder 跟 replica 分佈）
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">range_id</span><span class="p">,</span><span class="w"> </span><span class="n">lease_holder</span><span class="p">,</span><span class="w"> </span><span class="n">replicas</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="k">FROM</span><span class="w"> </span><span class="n">crdb_internal</span><span class="p">.</span><span class="n">ranges</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="k">WHERE</span><span class="w"> </span><span class="k">table_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;orders&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="k">LIMIT</span><span class="w"> </span><span class="mi">5</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></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="c1">-- 看 cluster max-offset 設定
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="k">SHOW</span><span class="w"> </span><span class="k">CLUSTER</span><span class="w"> </span><span class="n">SETTING</span><span class="w"> </span><span class="n">server</span><span class="p">.</span><span class="n">clock</span><span class="p">.</span><span class="n">persist_upper_bound_interval</span><span class="p">;</span></span></span></code></pre></div><h3 id="rollback-邊界">Rollback 邊界</h3>
<p>HLC + Raft 對 rollback 的態度跟 PostgreSQL 不同：</p>
<ul>
<li>HLC 時鐘前進不可回滾 — 不能「改一下 max-offset 後重啟試試看」</li>
<li>Raft commit 不可回滾 — 一旦 majority ack、log entry 持久化</li>
<li>想還原業務狀態 <em>只能新交易補償</em>、不能 reverse Raft log</li>
</ul>
<p>實務上的影響：incident 時不要嘗試「強制回到舊版本」、應該走 transaction-level rollback / compensation。對應 <a href="/blog/backend/knowledge-cards/transaction-boundary/" data-link-title="Transaction Boundary" data-link-desc="說明哪些資料變更應在同一個交易中一起成功或一起回復">transaction boundary 卡</a> 跟業務層補償設計。</p>
<h2 id="失敗模式clock-skew--majority-lost--hot-range--retry-storm">失敗模式：clock skew / majority lost / hot range / retry storm</h2>
<h3 id="clock-skew-panic">Clock skew panic</h3>
<p>最常見：NTP 服務掛、節點時鐘漂移超過 max-offset、節點自動 panic。production incident 訊號：</p>
<ul>
<li><code>chronyc tracking</code> 顯示 offset 持續成長</li>
<li>CockroachDB log 出現 <code>clock synchronization error</code></li>
<li>Prometheus metric <code>clock_offset_meannanos</code> 接近 max-offset</li>
</ul>
<p>修法：先恢復 NTP service、節點重啟前再次驗證時鐘已同步、不要動 <code>--max-offset</code>。對比 PostgreSQL primary 不關心 time skew、distributed SQL 把時鐘變成 first-class operational concern。</p>
<h3 id="raft-majority-lost">Raft majority lost</h3>
<p>3 節點 cluster 失去 2 個、剩 1 個無法 commit、cluster 全 read-only（甚至連 read 都可能受影響、因為 leaseholder 拿不到 valid lease）。對比 PostgreSQL primary 失效後 streaming replica 仍可 read、CockroachDB 的 fault tolerance 是 <em>quorum-based</em>、不是 <em>primary-replica</em>。</p>
<p>production 規劃要點：跨 AZ / region 分佈時、必須保證任何 <em>單一 failure domain</em> 失敗後仍有 majority 存活。3 節點配 1 AZ → AZ 失敗 = cluster down。最小 production 配置是 3 AZ × 1 node 或 3 region × 3 node。</p>
<h3 id="hot-rangeleaseholder-節點-cpu-飽和">Hot range：leaseholder 節點 CPU 飽和</h3>
<p>某個 range 寫流量集中（例：訂單 table primary key 是時間序 / 自增 ID）、leaseholder 節點變成熱點。徵兆：</p>
<ul>
<li>CockroachDB Console「Leaseholder count per node」分佈不均</li>
<li>某節點 CPU 飽和、其他節點閒置</li>
<li><code>crdb_internal.ranges</code> 顯示該 range 的 QPS 遠高於其他 range</li>
</ul>
<p>修法：</p>
<ul>
<li>手動 <code>ALTER TABLE ... SPLIT AT VALUES (...)</code> 強制 split hot range</li>
<li>改 primary key 設計、避免時間序 / 自增 ID（用 UUID / hash-prefixed key）</li>
<li>partition by region、把 hot range 切到不同 region 的 leaseholder</li>
</ul>
<h3 id="transaction-retry-storm">Transaction retry storm</h3>
<p>serializable contention 嚴重時 application 端 retry loop、CPU 雪崩。這個議題的 application contract 重塑屬獨立議題、見 <a href="../transaction-retry-pattern/">transaction retry pattern</a>。</p>
<h3 id="range-split--rebalance-期間-p99-spike">Range split / rebalance 期間 p99 spike</h3>
<p>自動 split 大 range、leaseholder 換手期間有 ~100ms 的 lease transfer 視窗、p99 短期 spike。production 訊號：CockroachDB Console「Rebalance queue size」非零 + p99 latency 同期波動。一般是良性 — rebalance 完就回穩。但連續波動代表 range 在「split → 寫熱 → 再 split」循環、要從 schema 層解。</p>
<h2 id="容量與觀測per-cluster-顆粒--來源分層">容量與觀測：per-cluster 顆粒 + 來源分層</h2>
<h3 id="必看-metric">必看 metric</h3>
<ul>
<li><code>Raft log queue size</code>：Raft replication 延遲訊號、持續高代表 follower 跟不上</li>
<li><code>Range count per node</code>：range 分佈是否均勻、不均代表 placement 有偏</li>
<li><code>Leaseholder count per node</code>：leaseholder 分佈是否均勻、不均直接導致 CPU 熱點</li>
<li><code>HLC offset distribution</code>：時鐘同步健康</li>
<li><code>Transaction retry rate</code>：contention 訊號（細節在 <a href="../transaction-retry-pattern/">transaction retry pattern</a>）</li>
</ul>
<h3 id="per-cluster-容量規劃顆粒9c40-netflix-揭露f47">Per-cluster 容量規劃顆粒（9.C40 Netflix 揭露、F4.7）</h3>
<p>Netflix 的 380+ cluster 模型揭露一個反直覺結論：production scale 不是「全公司一條容量曲線」、而是 <em>artery of small DBs</em>。每個 cluster 對應一個 application boundary、cluster sizing 從幾個 node 到 60 nodes 不等、最大單區 60 nodes / 26.5 TB（case 觀察段表格揭露）。</p>
<p>容量規劃顆粒對齊 application boundary 的好處：</p>
<ul>
<li>每個 cluster 各自規劃 capacity、不必預測「全公司加總 QPS」</li>
<li>blast radius 限縮在單一 app — 某 cluster 撞 hot range / Raft majority lost、其他 cluster 不受影響</li>
<li>upgrade / backup 可分批跑、不必整廠 maintenance window</li>
</ul>
<p>但也帶來 ops 成本：380+ cluster 需要 <em>專屬 Database Platform Team</em>（含 backup、upgrade、incident response、capacity review）— Netflix case 直接揭露這個前置條件。沒這量級團隊就走 Cockroach Cloud managed、不要 self-host。</p>
<p>per-app cluster vs shared cluster 的決策軸主寫於 <a href="../aurora-dsql-spanner-decision-tree/">aurora-dsql-spanner-decision-tree</a>、本篇 cross-link 不展開。</p>
<h3 id="寫入-latency-預算屬通用工程估算case-未揭露具體數字">寫入 latency 預算（屬通用工程估算、case 未揭露具體數字）</h3>
<p>以下數字屬通用工程估算 / 物理光速下界推導、<strong>DoorDash / Netflix / Hard Rock 三個 direct case 都沒揭露單一 cluster p99 latency</strong>。引用時必須明示來源層次：</p>
<ul>
<li>single-region 3-replica write p99 3-5ms（通用估算、跨 AZ Raft round trip）</li>
<li>multi-region 跨洲 write p99 100-150ms（光速下界 — 跨洲 round trip 物理 ~70-80ms × 2）</li>
<li>單一 range 寫 throughput ~1000 QPS（通用估算、實際依 row size / contention 而定）</li>
<li>整 cluster scale-out 加 range、寫入吞吐近線性擴展（理論、實際依 hot range 分佈）</li>
</ul>
<p>這些是「合理的工程估算量級」、不是 case 揭露的 p99 數字。讀者用這些做容量規劃時、應該 <em>自己 benchmark</em> 而不是直接套。</p>
<h3 id="doordash-1636-m-qps-引用紀律f41case-自帶警示">DoorDash 1.636 M QPS 引用紀律（F4.1、case 自帶警示）</h3>
<p>DoorDash case 揭露的 1.636 M QPS 是 <em>Aurora Postgres single-primary 在 2020-04-17 高峰撞牆的痛點</em>（multi-hour outage）、<strong>不是 CockroachDB throughput claim</strong>。case 明確警告不要把這個數字當「CockroachDB 撐 1.636 M QPS 的證據」。case 沒揭露遷移後單一 CockroachDB cluster 的峰值、只說「跑更多 cluster、alert volume 反而下降」。</p>
<p>引用這個數字時的口徑：</p>
<ul>
<li>寫成「Aurora 撞牆訊號」、不寫成「CockroachDB 容量證明」</li>
<li>single-primary 撞牆的轉折點是 <em>primary CPU + WAL flush rate</em>（DoorDash 策略段 1）、不是 IOPS</li>
<li>「換引擎」前先評估「兩階段紓壓」— DoorDash 路徑是先把 hot table 拆到獨立 Aurora cluster（紓壓）、再規劃 Aurora → CockroachDB 換引擎（<a href="/blog/backend/01-database/database-migration-playbook/" data-link-title="1.6 資料庫轉換實作：雙寫、回填、切流與回滾" data-link-desc="同 DB 內 schema 演進與資料變更的可分段驗證流程、跟 1.12 cross-DB migration 分工">1.6 database migration playbook</a>）</li>
</ul>
<h3 id="回路徑">回路徑</h3>
<ul>
<li><a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">9.5 瓶頸定位流程</a> 判斷 Raft-bound vs storage-bound</li>
<li><a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">9.6 容量規劃模型</a> replication factor × latency budget</li>
<li><a href="/blog/backend/knowledge-cards/latency-budget/" data-link-title="Latency Budget" data-link-desc="把 user-perceived latency 拆到每個 stage 的配額、反推架構選擇">latency budget 卡</a> cross-region quorum 預算</li>
</ul>
<h2 id="邊界與整合">邊界與整合</h2>
<h3 id="sibling-deep-articles">Sibling deep articles</h3>
<ul>
<li><a href="../survival-goals/">CockroachDB survival goals</a>：Raft replica 怎麼分佈到 zone / region、決定 RTO / RPO</li>
<li><a href="../transaction-retry-pattern/">CockroachDB transaction retry pattern</a>：serializable default 對 application 契約的重塑</li>
<li><a href="../locality-aware-schema/">CockroachDB locality-aware schema</a>：range placement 控制 + locality 配置</li>
</ul>
<h3 id="跟-aurora-對照">跟 Aurora 對照</h3>
<p>Aurora 是 <em>storage-level quorum</em>（4 of 6 storage replica）、compute 仍是 single primary。CockroachDB 是 <em>range-level Raft</em>（每個 range 獨立 majority）、compute 跟 storage 在每節點。兩者解的是不同 layer 的 consensus、結果是 Aurora 寫入仍受 primary 限制、CockroachDB 寫入隨節點線性擴。</p>
<h3 id="aurora-dsql--spanner-對比">Aurora DSQL / Spanner 對比</h3>
<p>完整三家 distributed SQL 對比、撞牆訊號分型、PostgreSQL 相容性 audit、團隊規模 vs vendor sizing barrier 等議題在 <a href="../aurora-dsql-spanner-decision-tree/">aurora-dsql-spanner-decision-tree</a>。</p>
<h3 id="1x-章節互引">1.x 章節互引</h3>
<ul>
<li><a href="/blog/backend/01-database/global-distributed-oltp/" data-link-title="1.11 全球分散式 OLTP" data-link-desc="Spanner / Aurora DSQL / Cosmos DB multi-region write / CockroachDB / TiDB 的全球一致性取捨">1.11 全球分散式 OLTP</a> 上游選型</li>
<li><a href="/blog/backend/01-database/transaction-boundary/" data-link-title="1.3 Transaction 與一致性邊界" data-link-desc="交易邊界、isolation level、retry 策略、distributed transaction（2PC、Saga）與跨 region 強一致取捨">1.3 Transaction Boundary</a> distributed transaction 邊界</li>
</ul>
<h3 id="何時不用本文">何時不用本文</h3>
<ul>
<li>single-region OLTP + 寫入未撞 PostgreSQL primary 天花板 → PostgreSQL 已足夠</li>
<li>對 cross-region quorum 100-150ms latency 預算無法接受 → 走 async replication 路線</li>
<li>沒 NTP 維運能力 → distributed SQL 把時鐘變 ops concern、沒準備好不要硬上</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li><a href="/blog/backend/01-database/vendors/cockroachdb/" data-link-title="CockroachDB" data-link-desc="分散式 SQL、PostgreSQL 相容、跨區強一致、Spanner 的開源 / 跨雲替代">CockroachDB vendor overview</a></li>
<li><a href="/blog/backend/09-performance-capacity/cases/doordash-cockroachdb-orders-platform/" data-link-title="9.C39 DoorDash：Aurora Postgres 寫入瓶頸 → CockroachDB 多主寫入" data-link-desc="DoorDash 從 Aurora Postgres 遷到 CockroachDB、解 1.6 M QPS 單主寫入瓶頸、外送平台爆量壓力下重做 OLTP 拓樸">9.C39 DoorDash</a>（Aurora 1.636 M QPS 撞牆訊號）</li>
<li><a href="/blog/backend/09-performance-capacity/cases/netflix-cockroachdb-multi-region-fleet/" data-link-title="9.C40 Netflix：380&#43; CockroachDB cluster 的 multi-active 拓樸艦隊" data-link-desc="Netflix 把 Cassandra 不夠用的 transactional workload 移到 CockroachDB、380&#43; cluster / 60&#43; 跨 region、含 Open Connect、studio cloud drive、gaming control plane">9.C40 Netflix</a>（380+ cluster artery of small DBs）</li>
<li><a href="/blog/backend/09-performance-capacity/cases/spanner-planetary-scale-database-gcp/" data-link-title="9.C10 Cloud Spanner：每秒 10 億請求的全球一致性資料庫" data-link-desc="Google Cloud Spanner 內部峰值 10 億 req/sec、跨地區強一致 — 全球分散式 OLTP 容量參考">9.C10 Spanner planetary scale</a>（TrueTime 對照）</li>
<li><a href="/blog/backend/knowledge-cards/distributed-sql/" data-link-title="Distributed SQL" data-link-desc="把 SQL 與交易語意延伸到多節點與多區域的資料庫形態">distributed SQL 卡</a></li>
<li><a href="/blog/backend/knowledge-cards/quorum/" data-link-title="Quorum" data-link-desc="分散式系統以多數節點同意作為提交或讀取有效性的門檻">quorum 卡</a></li>
<li>官方：<a href="https://www.cockroachlabs.com/docs/stable/architecture/overview.html">CockroachDB Architecture</a> / <a href="https://cse.buffalo.edu/tech-reports/2014-04.pdf">Hybrid Logical Clocks paper</a> / <a href="https://raft.github.io/raft.pdf">Raft paper</a></li>
</ul>
]]></content:encoded></item></channel></rss>