<?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>Query-Boundary on Tarragon</title><link>https://tarrragon.github.io/blog/tags/query-boundary/</link><description>Recent content in Query-Boundary on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 13 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/query-boundary/index.xml" rel="self" type="application/rss+xml"/><item><title>1.8 State Ownership 與 Query Boundary</title><link>https://tarrragon.github.io/blog/backend/01-database/state-ownership-query-boundary/</link><pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/state-ownership-query-boundary/</guid><description>&lt;p>State ownership 與 query boundary 的核心責任是先定義資料由誰承擔正式判斷、再定義不同查詢路徑能回答什麼問題。進入 MySQL、PostgreSQL、MSSQL 或其他資料庫前、讀者需要先知道資料庫同時是儲存工具與服務狀態的責任邊界。&lt;/p>
&lt;p>本章從 source of truth 的責任分層開始、引入 CQRS / event sourcing / materialized view 等模式、最後處理四種 query 邊界的設計。讀完後讀者能回答：哪些資料是正式狀態、什麼時候該分讀寫 model、materialized view 怎麼用、replica lag 怎麼影響 query。&lt;/p>
&lt;h2 id="state-ownership">State Ownership&lt;/h2>
&lt;p>State ownership 的責任是判斷哪些資料是 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth&lt;/a>、哪些資料屬於 cache、search index、event log 或報表副本。正式狀態會影響交易結果、權限判斷、對帳與客服修復、因此需要清楚的 owner、schema、驗證方式與變更流程。&lt;/p>
&lt;p>訂單狀態、付款狀態、會員方案、權限授權與發票紀錄通常屬於正式狀態。商品搜尋索引、快取值、統計摘要與推薦結果通常是派生狀態；派生狀態可以錯過短暫更新、但正式狀態需要能被追溯、修復與稽核。&lt;/p>
&lt;h2 id="canonical-state-vs-derived-state">Canonical State vs Derived State&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>Canonical state&lt;/th>
 &lt;th>Derived state&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>角色&lt;/td>
 &lt;td>source of truth&lt;/td>
 &lt;td>從 canonical 計算 / 同步&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>寫入&lt;/td>
 &lt;td>用戶 / 業務操作&lt;/td>
 &lt;td>從 canonical 推&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>一致性&lt;/td>
 &lt;td>strong / serializable&lt;/td>
 &lt;td>eventual 通常夠用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>修復&lt;/td>
 &lt;td>必須能精確修復&lt;/td>
 &lt;td>可以「砍掉重建」&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>範例&lt;/td>
 &lt;td>訂單、付款、餘額&lt;/td>
 &lt;td>搜尋 index、recommendation、daily summary&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Canonical state 的特徵&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>業務決策依據（付款、權限）&lt;/li>
&lt;li>不能從其他地方重建（一旦丟、無法找回）&lt;/li>
&lt;li>需要 audit log、point-in-time recovery、backup&lt;/li>
&lt;li>通常在 OLTP DB（PostgreSQL / Aurora / Spanner）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Derived state 的特徵&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>從 canonical 推算出來&lt;/li>
&lt;li>可以「rebuild」（lazy 或 eager）&lt;/li>
&lt;li>失效可接受（用戶可能看到舊的）&lt;/li>
&lt;li>通常在 cache / search / analytics store&lt;/li>
&lt;li>對應案例：&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/tinder-elasticache-valkey-matching/" data-link-title="9.C6 Tinder：ElastiCache for Valkey 撐 4700 萬月活的配對引擎" data-link-desc="Tinder 用 Amazon ElastiCache for Valkey 提供配對引擎所需的次毫秒延遲快取層">9.C6 Tinder ElastiCache&lt;/a> 配對快取、&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/tubi-elasticache-ml-feature-store/" data-link-title="9.C25 Tubi：從 ScyllaDB 遷到 ElastiCache、ML feature store 達 sub-10ms p99" data-link-desc="Tubi 把 ML 推薦的 feature store 從 ScyllaDB 遷到 ElastiCache for Redis、99 百分位延遲降到 10ms 以下">9.C25 Tubi ML feature store&lt;/a> feature&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>設計原則&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>同一資料 &lt;em>不能&lt;/em> 同時是兩個地方的 canonical → 衝突時不知道信誰&lt;/li>
&lt;li>寫入永遠先寫 canonical、再 propagate 到 derived&lt;/li>
&lt;li>derived 出錯只能 rebuild、不能拿來「修正 canonical」&lt;/li>
&lt;/ul>
&lt;h2 id="cqrs-在資料庫情境的應用">CQRS 在資料庫情境的應用&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/cqrs/" data-link-title="CQRS" data-link-desc="說明讀寫不對稱時為何需要分離查詢與寫入責任、分離的判準與代價">CQRS&lt;/a> 的概念定義、設計判準與代價見知識卡。本段聚焦在資料庫層面：state ownership 的決策如何影響你要不要分離讀寫模型。&lt;/p>
&lt;p>State ownership 跟 CQRS 的交叉點是：當 canonical state 的 schema 為寫入正確性最佳化（normalize、強一致、transaction boundary 清楚），但讀取面的多種消費者各自需要不同的反正規化形狀（列表頁要扁平 summary、報表要聚合、搜尋要全文索引），canonical schema 無法同時服務這些讀取需求。這時候分離 write model 跟 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/read-model/" data-link-title="Read Model" data-link-desc="說明為查詢場景建立的讀取模型，與正式狀態的責任分離">read model&lt;/a> 是解決形狀不對稱的方式。&lt;/p></description><content:encoded><![CDATA[<p>State ownership 與 query boundary 的核心責任是先定義資料由誰承擔正式判斷、再定義不同查詢路徑能回答什麼問題。進入 MySQL、PostgreSQL、MSSQL 或其他資料庫前、讀者需要先知道資料庫同時是儲存工具與服務狀態的責任邊界。</p>
<p>本章從 source of truth 的責任分層開始、引入 CQRS / event sourcing / materialized view 等模式、最後處理四種 query 邊界的設計。讀完後讀者能回答：哪些資料是正式狀態、什麼時候該分讀寫 model、materialized view 怎麼用、replica lag 怎麼影響 query。</p>
<h2 id="state-ownership">State Ownership</h2>
<p>State ownership 的責任是判斷哪些資料是 <a href="/blog/backend/knowledge-cards/source-of-truth/" data-link-title="Source of Truth" data-link-desc="說明正式資料來源如何決定資料判斷、修復與一致性責任">source of truth</a>、哪些資料屬於 cache、search index、event log 或報表副本。正式狀態會影響交易結果、權限判斷、對帳與客服修復、因此需要清楚的 owner、schema、驗證方式與變更流程。</p>
<p>訂單狀態、付款狀態、會員方案、權限授權與發票紀錄通常屬於正式狀態。商品搜尋索引、快取值、統計摘要與推薦結果通常是派生狀態；派生狀態可以錯過短暫更新、但正式狀態需要能被追溯、修復與稽核。</p>
<h2 id="canonical-state-vs-derived-state">Canonical State vs Derived State</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Canonical state</th>
          <th>Derived state</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>角色</td>
          <td>source of truth</td>
          <td>從 canonical 計算 / 同步</td>
      </tr>
      <tr>
          <td>寫入</td>
          <td>用戶 / 業務操作</td>
          <td>從 canonical 推</td>
      </tr>
      <tr>
          <td>一致性</td>
          <td>strong / serializable</td>
          <td>eventual 通常夠用</td>
      </tr>
      <tr>
          <td>修復</td>
          <td>必須能精確修復</td>
          <td>可以「砍掉重建」</td>
      </tr>
      <tr>
          <td>範例</td>
          <td>訂單、付款、餘額</td>
          <td>搜尋 index、recommendation、daily summary</td>
      </tr>
  </tbody>
</table>
<p><strong>Canonical state 的特徵</strong>：</p>
<ul>
<li>業務決策依據（付款、權限）</li>
<li>不能從其他地方重建（一旦丟、無法找回）</li>
<li>需要 audit log、point-in-time recovery、backup</li>
<li>通常在 OLTP DB（PostgreSQL / Aurora / Spanner）</li>
</ul>
<p><strong>Derived state 的特徵</strong>：</p>
<ul>
<li>從 canonical 推算出來</li>
<li>可以「rebuild」（lazy 或 eager）</li>
<li>失效可接受（用戶可能看到舊的）</li>
<li>通常在 cache / search / analytics store</li>
<li>對應案例：<a href="/blog/backend/09-performance-capacity/cases/tinder-elasticache-valkey-matching/" data-link-title="9.C6 Tinder：ElastiCache for Valkey 撐 4700 萬月活的配對引擎" data-link-desc="Tinder 用 Amazon ElastiCache for Valkey 提供配對引擎所需的次毫秒延遲快取層">9.C6 Tinder ElastiCache</a> 配對快取、<a href="/blog/backend/09-performance-capacity/cases/tubi-elasticache-ml-feature-store/" data-link-title="9.C25 Tubi：從 ScyllaDB 遷到 ElastiCache、ML feature store 達 sub-10ms p99" data-link-desc="Tubi 把 ML 推薦的 feature store 從 ScyllaDB 遷到 ElastiCache for Redis、99 百分位延遲降到 10ms 以下">9.C25 Tubi ML feature store</a> feature</li>
</ul>
<p><strong>設計原則</strong>：</p>
<ul>
<li>同一資料 <em>不能</em> 同時是兩個地方的 canonical → 衝突時不知道信誰</li>
<li>寫入永遠先寫 canonical、再 propagate 到 derived</li>
<li>derived 出錯只能 rebuild、不能拿來「修正 canonical」</li>
</ul>
<h2 id="cqrs-在資料庫情境的應用">CQRS 在資料庫情境的應用</h2>
<p><a href="/blog/backend/knowledge-cards/cqrs/" data-link-title="CQRS" data-link-desc="說明讀寫不對稱時為何需要分離查詢與寫入責任、分離的判準與代價">CQRS</a> 的概念定義、設計判準與代價見知識卡。本段聚焦在資料庫層面：state ownership 的決策如何影響你要不要分離讀寫模型。</p>
<p>State ownership 跟 CQRS 的交叉點是：當 canonical state 的 schema 為寫入正確性最佳化（normalize、強一致、transaction boundary 清楚），但讀取面的多種消費者各自需要不同的反正規化形狀（列表頁要扁平 summary、報表要聚合、搜尋要全文索引），canonical schema 無法同時服務這些讀取需求。這時候分離 write model 跟 <a href="/blog/backend/knowledge-cards/read-model/" data-link-title="Read Model" data-link-desc="說明為查詢場景建立的讀取模型，與正式狀態的責任分離">read model</a> 是解決形狀不對稱的方式。</p>
<p>資料庫情境的 CQRS 有不同的實作強度：</p>
<p><strong>最輕量 — 同 DB 不同 query path</strong>：寫入走 canonical table，讀取走 <a href="/blog/backend/knowledge-cards/materialized-view/" data-link-title="Materialized View" data-link-desc="說明預先計算並儲存查詢結果以加速讀取的資料結構">materialized view</a> 或反正規化 view。同一個 PostgreSQL 裡用 materialized view 就能實現最基本的讀寫分離，不需要兩個 DB、不需要事件同步。適合讀寫形狀不同但流量規模還不需要獨立擴展的階段。</p>
<p><strong>中度 — 同 DB 加 read replica</strong>：寫入走 primary，列表跟報表走 read replica。Replica lag 決定哪些 query 能走 replica（見下方 Replica Lag 段）。適合讀取流量開始壓迫寫入的階段。</p>
<p><strong>完整 — 獨立 read store</strong>：寫入走 OLTP DB，讀取走獨立的 analytics store（BigQuery、Athena）或搜尋引擎（Elasticsearch）。透過 CDC 或事件同步維護 read store。適合讀取形狀、流量、SLA 都跟寫入完全不同的階段。</p>
<p>對應案例：<a href="/blog/backend/09-performance-capacity/cases/bookmyshow-indian-ticketing-platform/" data-link-title="9.C17 BookMyShow：印度年售 2 億張票的資料架構現代化" data-link-desc="BookMyShow 從 15 年自建 analytics 遷移到 AWS modern data architecture、4 個月完成、分析成本下降 80%">9.C17 BookMyShow</a> — 交易層（OLTP）跟資料層（BigQuery / Athena）分開。<a href="/blog/backend/09-performance-capacity/cases/wayfair-gcp-burst-capacity/" data-link-title="9.C22 Wayfair：用 GCP 提供 Way Day / Black Friday 的 burst capacity" data-link-desc="Wayfair 22M&#43; 商品 &#43; 16,000&#43; 供應商、用 GCP 補充 on-prem data center 在峰值事件的 burst capacity">9.C22 Wayfair</a> — on-prem OLTP + GCP BigQuery analytics。</p>
<h2 id="event-sourcing-與-state-ownership">Event Sourcing 與 State Ownership</h2>
<p><a href="/blog/backend/knowledge-cards/event-sourcing/" data-link-title="Event Sourcing" data-link-desc="說明用 append-only 事件流取代 mutable state 作為正式紀錄的設計模式、需求判準與代價">Event sourcing</a> 的概念定義、設計判準與代價見知識卡。本段聚焦在資料庫層面：event sourcing 怎麼改變 state ownership 跟 query boundary。</p>
<p>Event sourcing 把 state ownership 的正式紀錄從 mutable row 改成 append-only <a href="/blog/backend/knowledge-cards/event-log/" data-link-title="Event Log" data-link-desc="說明事件歷史如何保存、重播與支援跨服務資料重建">event log</a>。這個改變影響本章的每一個面向：</p>
<p><strong>對 canonical / derived 分類的影響</strong>：採用 event sourcing 後，event log 是 canonical state，current state 變成 derived state。這跟傳統 CRUD 架構相反 — 傳統架構中 current state（mutable row）是 canonical，歷史紀錄（audit log）是 derived。</p>
<p><strong>對 query boundary 的影響</strong>：event log 不適合直接服務交易查詢跟列表查詢（每次 replay 整條事件流太慢）。Event sourcing 幾乎必然搭配 <a href="/blog/backend/knowledge-cards/projection/" data-link-title="Projection" data-link-desc="說明從事件流或資料變更推算出查詢用讀取視圖的轉換機制">projection</a> 維護 read model — projection 持續消費事件流、更新反正規化的查詢 view。交易查詢讀 projection 的輸出而非直接讀 event log。</p>
<p><strong>對修復流程的影響</strong>：傳統架構的資料修復是「直接改 row」；event sourcing 的修復是「發一筆補償事件（compensating event）」。修復本身也是事件、會被記錄在 event log 裡、提供完整的修復 audit trail。</p>
<p>Event sourcing 的設計門檻在於 projection 的維護跟 event schema evolution。Projection 數量增長後，每次 event schema 改版都需要同步更新所有 projection；projection 的 replay 跟 reconciliation 是長期運維的主要成本。這些代價決定了 event sourcing 適合「需要完整變更歷史」的業務場景（金融帳務、訂單流程、法規合規），而非所有資料存取場景。</p>
<h2 id="materialized-view-在資料庫的應用">Materialized View 在資料庫的應用</h2>
<p><a href="/blog/backend/knowledge-cards/materialized-view/" data-link-title="Materialized View" data-link-desc="說明預先計算並儲存查詢結果以加速讀取的資料結構">Materialized view</a> 的概念定義見知識卡。本段聚焦在 OLTP 資料庫裡 materialized view 作為最輕量 read model 的具體實作。</p>
<p>Materialized view 是「同 DB 內最簡單的讀寫分離」。不需要事件同步、不需要獨立 read store、不需要 projection consumer — 資料庫自己定期執行查詢、存放結果。</p>
<p><strong>跟 regular view 的差別</strong>：regular view 是 SQL 別名，每次 query 重跑底層查詢；materialized view 有實體儲存，query 時直接讀預計算結果。差別在 query-time cost — 複雜 JOIN / aggregation 重複跑時，materialized view 把計算推到 refresh 時、query 時接近零成本。</p>
<p><strong>Refresh 策略</strong>：</p>
<ul>
<li><strong>全量 refresh</strong>：PostgreSQL 的 <code>REFRESH MATERIALIZED VIEW</code>，refresh 期間 view 預設 unavailable。</li>
<li><strong>Concurrent refresh</strong>：PostgreSQL 的 <code>CONCURRENTLY</code> 模式，refresh 期間 view 仍可讀但資料可能 stale。</li>
<li><strong>增量 refresh</strong>：PostgreSQL 的 <code>pg_ivm</code>、Oracle 的 fast refresh — 只更新變更的部分，成本低但配置複雜。</li>
<li><strong>Trigger-based</strong>：特定 event 觸發 refresh，適合低頻變更的資料。</li>
</ul>
<p><strong>在 state ownership 的定位</strong>：materialized view 是 derived state，修復方式是 refresh（重建）而非直接修改。大量 materialized view 會拖累寫入吞吐 — 每次 base table 變更都可能觸發 refresh 計算。設計時要平衡 refresh 頻率跟 query freshness 需求。</p>
<p><strong>跟觀測領域的對照</strong>：觀測領域的 <a href="/blog/backend/knowledge-cards/recording-rule/" data-link-title="Recording Rule" data-link-desc="說明把 query-time 聚合計算推到寫入時的 pre-aggregation 機制">recording rule</a> 在概念上等同於 TSDB 層的 materialized view — 定期執行 query expression、把結果寫成新 series。兩者面對同樣的設計問題：refresh 頻率、freshness lag、維護成本與儲存增長。觀測領域的 CQRS 特化應用見 <a href="/blog/backend/04-observability/observability-query-design/" data-link-title="4.23 觀測查詢設計" data-link-desc="把觀測資料的讀取路徑當系統設計問題處理：三種查詢模式、storage tiering、pre-aggregation 與資源治理">4.23 觀測查詢設計</a>。</p>
<h2 id="query-boundary-四種">Query Boundary 四種</h2>
<p>Query boundary 的責任是讓不同查詢路徑承擔不同服務問題。交易查詢、列表查詢、報表查詢與對帳查詢都可能讀同一張表、但它們的正確性、延遲與資料新鮮度要求不同。</p>
<table>
  <thead>
      <tr>
          <th>查詢類型</th>
          <th>服務責任</th>
          <th>典型 latency</th>
          <th>容忍 stale</th>
          <th>風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>交易查詢</td>
          <td>支援使用者當下動作、例如付款、下單、授權</td>
          <td>&lt; 100ms</td>
          <td>不容忍</td>
          <td>延遲或錯誤會直接影響交易結果</td>
      </tr>
      <tr>
          <td>列表查詢</td>
          <td>支援使用者瀏覽與管理、例如訂單列表、會員清單</td>
          <td>&lt; 500ms</td>
          <td>可容忍秒級</td>
          <td>可能放大 index、pagination 與排序成本</td>
      </tr>
      <tr>
          <td>報表查詢</td>
          <td>支援營運分析、財務統計與趨勢判讀</td>
          <td>秒到分鐘級</td>
          <td>可容忍 hour 級</td>
          <td>容易壓迫線上資料庫與混淆資料時效</td>
      </tr>
      <tr>
          <td>對帳查詢</td>
          <td>驗證正式狀態與外部事實是否一致</td>
          <td>分鐘到小時級</td>
          <td>視業務</td>
          <td>查詢定義錯誤會造成錯修或漏修</td>
      </tr>
  </tbody>
</table>
<p>這四種查詢混在一起時、資料庫會同時承擔低延遲交易與高成本分析、最後讓任何一種資料庫選型都變得模糊。</p>
<h3 id="交易路徑的邊界">交易路徑的邊界</h3>
<p>交易路徑的責任是維持使用者動作的即時正確性。它需要短查詢、明確 index、可控 <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 強一致取捨">transaction boundary</a> 與清楚 timeout。</p>
<p>交易路徑的設計要把報表聚合或長時間掃描移到其他查詢路徑。若下單 API 同時查歷史報表、計算大範圍統計或同步重建派生狀態、交易延遲會被非交易責任拖慢。</p>
<p>對應 <a href="/blog/backend/09-performance-capacity/cases/draftkings-aurora-financial-ledger/" data-link-title="9.C4 DraftKings：Aurora 撐 100 萬 ops/min 的體育博彩金融帳本" data-link-desc="DraftKings 用 Aurora MySQL 跑體育博彩金融帳本、Super Bowl 流量 &#43;50% 不影響延遲">9.C4 DraftKings</a> — 200 個獨立 Aurora cluster 把不同業務 transaction 分開、避免互相影響。</p>
<h3 id="列表與報表的邊界">列表與報表的邊界</h3>
<p>列表查詢的責任是支援產品體驗中的瀏覽與定位。列表查詢需要穩定排序、分頁策略、篩選條件與查詢成本界線；它應建立自己的讀取模型或索引策略、避免直接借用交易查詢的資料模型造成 slow query、排序漂移與 pagination 重複。</p>
<p>報表查詢的責任是支援分析與決策。報表通常可以接受資料延遲、因此更適合使用 read replica、materialized view、ETL 或 analytics store。把報表直接壓在線上 primary 上、會讓交易服務承擔不必要的容量風險。</p>
<p>對應 <a href="/blog/backend/09-performance-capacity/cases/wayfair-gcp-burst-capacity/" data-link-title="9.C22 Wayfair：用 GCP 提供 Way Day / Black Friday 的 burst capacity" data-link-desc="Wayfair 22M&#43; 商品 &#43; 16,000&#43; 供應商、用 GCP 補充 on-prem data center 在峰值事件的 burst capacity">9.C22 Wayfair hybrid burst</a>、<a href="/blog/backend/09-performance-capacity/cases/bookmyshow-indian-ticketing-platform/" data-link-title="9.C17 BookMyShow：印度年售 2 億張票的資料架構現代化" data-link-desc="BookMyShow 從 15 年自建 analytics 遷移到 AWS modern data architecture、4 個月完成、分析成本下降 80%">9.C17 BookMyShow</a> — 交易層跟資料層分開部署。</p>
<h3 id="對帳查詢的邊界">對帳查詢的邊界</h3>
<p>對帳查詢的責任是驗證正式狀態是否與外部事實一致。付款、發票、庫存與訂閱方案都需要對帳查詢、但對帳查詢要保留時間窗、資料來源、差異定義與人工修復入口。</p>
<p>對帳查詢承擔比報表更直接的修復責任。報表回答「現在看起來如何」、對帳回答「哪一筆正式狀態需要修復」。因此對帳查詢結果要能進入 <a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">Observability Evidence Package</a> 與 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">Incident Decision Log</a>。</p>
<p>詳見 <a href="/blog/backend/01-database/reconciliation-data-repair/" data-link-title="1.9 Reconciliation 與 Data Repair" data-link-desc="資料不一致的分類、偵測模式、修復策略、audit trail、跟 backup / PITR 整合">1.9 Reconciliation 與 Data Repair</a>。</p>
<h2 id="replica-lag-對-query-boundary-的影響">Replica Lag 對 Query Boundary 的影響</h2>
<p>當應用使用 read replica 擴 read traffic 時、replica lag 會直接影響 query boundary 設計。</p>
<p><strong>典型 lag</strong>：</p>
<ul>
<li>PostgreSQL streaming：&lt; 100ms（同 AZ）</li>
<li>Aurora：10-30ms（同 region）</li>
<li>跨 region replica：秒級到分鐘級</li>
</ul>
<p><strong>不同 query 對 lag 的容忍</strong>：</p>
<ul>
<li>交易查詢：不可容忍 lag、必須走 primary</li>
<li>read-after-write（剛寫完查自己）：必須 primary、或 session sticky</li>
<li>列表查詢：通常容忍 lag &lt; 1 秒</li>
<li>報表查詢：lag 分鐘級可接受</li>
<li>對帳查詢：通常用 batch、lag 不關鍵</li>
</ul>
<p><strong>Stale read 容忍策略</strong>：</p>
<ul>
<li>「能容忍秒級 stale」的 read → replica（用戶 profile、報表）</li>
<li>「不能 stale」的 read → primary（剛寫入後的查詢、餘額確認）</li>
<li>read-after-write：用 session token 標記「剛寫過」、N 秒內讀走 primary</li>
</ul>
<p>對應 <a href="/blog/backend/01-database/high-concurrency-access/" data-link-title="1.1 高併發下的 SQL 讀寫邊界" data-link-desc="說明高併發服務如何共用資料庫 client、控制 transaction、管理 connection pool、避免資料庫成為瓶頸">1.1 高併發資料存取</a> 的「Read Replica Scaling」段。</p>
<h2 id="選型前判準">選型前判準</h2>
<p>資料庫選型前要先回答四個問題：</p>
<ol>
<li>哪些資料是正式狀態、哪些是派生狀態</li>
<li>哪些查詢屬於交易路徑、哪些可以延遲或離線化</li>
<li>哪些查詢結果會觸發修復、退款、補償或人工決策</li>
<li>哪些資料需要 audit、masking、retention 或刪除責任</li>
</ol>
<p>這些問題決定後續該比較 relational database、document database、search index、analytics store 還是 cache。工具差異要放在責任邊界之後討論。</p>
<h2 id="實體服務討論承接點">實體服務討論承接點</h2>
<p>實體資料庫文章要承接本篇的 state ownership 與 query boundary。PostgreSQL、MySQL、MSSQL 或其他 relational database 的比較、應先問它們如何支援正式狀態、交易查詢、列表查詢、報表查詢與對帳查詢、再進入索引、隔離層級、replica 或工具語法。</p>
<p>若主問題是正式狀態與交易一致性、後續文章要優先比較 transaction、isolation、index 與 migration 能力。若主問題是報表與搜尋、後續文章要評估 read replica、materialized view、search index 或 analytics store。若主問題是對帳與修復、後續文章要比較 <a href="/blog/backend/knowledge-cards/validation-query/" data-link-title="Validation Query" data-link-desc="說明遷移、回填與修復期間如何用查詢證明資料語意是否一致">validation query</a>、audit log、backup/restore 與資料修復流程。</p>
<h2 id="案例對照">案例對照</h2>
<table>
  <thead>
      <tr>
          <th>案例</th>
          <th>state / query 設計重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/draftkings-aurora-financial-ledger/" data-link-title="9.C4 DraftKings：Aurora 撐 100 萬 ops/min 的體育博彩金融帳本" data-link-desc="DraftKings 用 Aurora MySQL 跑體育博彩金融帳本、Super Bowl 流量 &#43;50% 不影響延遲">9.C4 DraftKings Aurora</a></td>
          <td>200 個獨立 cluster 隔離 transaction scope</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/bookmyshow-indian-ticketing-platform/" data-link-title="9.C17 BookMyShow：印度年售 2 億張票的資料架構現代化" data-link-desc="BookMyShow 從 15 年自建 analytics 遷移到 AWS modern data architecture、4 個月完成、分析成本下降 80%">9.C17 BookMyShow</a></td>
          <td>OLTP 交易層 + BigQuery / Athena 分析層</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/wayfair-gcp-burst-capacity/" data-link-title="9.C22 Wayfair：用 GCP 提供 Way Day / Black Friday 的 burst capacity" data-link-desc="Wayfair 22M&#43; 商品 &#43; 16,000&#43; 供應商、用 GCP 補充 on-prem data center 在峰值事件的 burst capacity">9.C22 Wayfair</a></td>
          <td>on-prem OLTP + GCP BigQuery 分析、典型 CQRS 配置</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/tubi-elasticache-ml-feature-store/" data-link-title="9.C25 Tubi：從 ScyllaDB 遷到 ElastiCache、ML feature store 達 sub-10ms p99" data-link-desc="Tubi 把 ML 推薦的 feature store 從 ScyllaDB 遷到 ElastiCache for Redis、99 百分位延遲降到 10ms 以下">9.C25 Tubi</a></td>
          <td>feature store（derived state）、跟 source 分離</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/09-performance-capacity/cases/disney-plus-content-metadata/" data-link-title="9.C27 Disney&#43;：DynamoDB 撐每日數十億動作的觀看歷史" data-link-desc="Disney&#43; 用 DynamoDB 撐每日數十億動作的觀看歷史、watchlist、播放進度等串流 metadata">9.C27 Disney+</a></td>
          <td>watch list（user state）跟 content metadata 分層</td>
      </tr>
  </tbody>
</table>
<h2 id="跨模組路由">跨模組路由</h2>
<ol>
<li>與 1.2 的交接：欄位與索引語意回到 <a href="/blog/backend/01-database/schema-design/" data-link-title="1.2 Schema Design 與資料建模" data-link-desc="整理 table、index、key、partition、denormalization 與命名規則">schema design</a></li>
<li>與 1.3 的交接：transaction boundary 設計影響哪些 query 走 primary、哪些可走 replica</li>
<li>與 1.7 的交接：正式狀態變更要進入 production rollout — <a href="/blog/backend/01-database/schema-migration-rollout-evidence/" data-link-title="1.7 Schema Migration Rollout 證據（Schema Migration Rollout Evidence）實作示範" data-link-desc="以訂單付款狀態欄位演進示範 schema migration 如何產出 evidence、release gate 與 incident decision log。">Schema Migration Rollout Evidence</a></li>
<li>與 1.9 的交接：對帳查詢的下游修復 — <a href="/blog/backend/01-database/reconciliation-data-repair/" data-link-title="1.9 Reconciliation 與 Data Repair" data-link-desc="資料不一致的分類、偵測模式、修復策略、audit trail、跟 backup / PITR 整合">Reconciliation and Data Repair</a></li>
<li>與 2 的交接：cache layer 是 derived state 最常見的形式 — <a href="/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">02 快取模組</a></li>
<li>與 4.20 的交接：query evidence 跟 reconciliation evidence — <a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">Observability Evidence Package</a></li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<p>要進一步處理 schema 與資料模型、接著讀 <a href="/blog/backend/01-database/schema-design/" data-link-title="1.2 Schema Design 與資料建模" data-link-desc="整理 table、index、key、partition、denormalization 與命名規則">1.2 schema design 與資料建模</a>。要處理 schema 演進與正式狀態變更、接著讀 <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> 跟 <a href="/blog/backend/01-database/schema-migration-rollout-evidence/" data-link-title="1.7 Schema Migration Rollout 證據（Schema Migration Rollout Evidence）實作示範" data-link-desc="以訂單付款狀態欄位演進示範 schema migration 如何產出 evidence、release gate 與 incident decision log。">1.7 Schema Migration Rollout 證據</a>。要處理對帳跟資料修復、接著讀 <a href="/blog/backend/01-database/reconciliation-data-repair/" data-link-title="1.9 Reconciliation 與 Data Repair" data-link-desc="資料不一致的分類、偵測模式、修復策略、audit trail、跟 backup / PITR 整合">1.9 Reconciliation</a>。要設計 KV / Document 的 state ownership、接著讀 <a href="/blog/backend/01-database/kv-document-capacity-planning/" data-link-title="1.10 KV / Document DB 容量規劃" data-link-desc="DynamoDB / Cosmos DB / Bigtable / MongoDB 等 KV / Document DB 的容量設計、partition key 取捨、capacity mode 選擇">1.10 KV / Document 容量規劃</a>。</p>
]]></content:encoded></item></channel></rss>