<?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>Overview on Tarragon</title><link>https://tarrragon.github.io/blog/tags/overview/</link><description>Recent content in Overview on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 15 Oct 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/overview/index.xml" rel="self" type="application/rss+xml"/><item><title>Backend 教材的閱讀路線：教材的分層、依讀者目的排的模組順序與貫穿各模組的 checkout 範例流程</title><link>https://tarrragon.github.io/blog/backend/reading-paths/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/reading-paths/</guid><description>&lt;p>這篇整理 Backend 教材各模組之間的閱讀順序：教材分成哪幾層、依讀者手上的目的各走哪一條路線，以及一條把各模組串起來的 checkout 流程。範圍是模組之間的順序，每個模組內部的章節順序在該模組的列表頁。&lt;/p>
&lt;h2 id="backend-教材的分層分類語言服務路徑與具體工具各自回答的問題">Backend 教材的分層：分類語言、服務路徑與具體工具各自回答的問題&lt;/h2>
&lt;p>Backend 教材把後端服務能力分成三層來教。&lt;strong>分類語言&lt;/strong>讓讀者知道資料、快取、事件、觀測、部署、驗證、資安、事故與容量各自承擔什麼責任；&lt;strong>服務路徑&lt;/strong>用一條具體業務流程，演練這些能力之間怎麼交接；&lt;strong>具體工具&lt;/strong>討論 PostgreSQL、Redis、Kafka、Kubernetes、PagerDuty、k6 這些服務落到真實操作時的能力、成本與遷移。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>層&lt;/th>
 &lt;th>回答的問題&lt;/th>
 &lt;th>從哪裡讀起&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>分類語言&lt;/td>
 &lt;td>這是哪一類問題&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/" data-link-title="模組零：後端服務選型" data-link-desc="從需求類型判斷資料庫、快取、訊息佇列、觀測與部署平台的選型方向">後端服務選型模組&lt;/a>與&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">前置知識卡片&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>服務路徑&lt;/td>
 &lt;td>一個服務流程怎麼把多個能力接起來&lt;/td>
 &lt;td>資料庫到容量規劃各模組的主章，加上本篇下方的 checkout 流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>具體工具&lt;/td>
 &lt;td>要選哪個工具、怎麼遷移&lt;/td>
 &lt;td>各模組的 &lt;code>vendors/&lt;/code> 子目錄；案例庫在&lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">資安與資料保護模組&lt;/a>與&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置在延遲目標內處理得了多少請求、要加多少機器』變成可量化、可驗證、可改進的工程流程">效能工程與容量規劃模組&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>三層的順序是理解的順序：分類語言先建立，服務路徑才有詞彙可以描述交接，具體工具的比較才有一個分類責任可以回扣。已經懂分類、只想比較工具的讀者可以直接從具體工具那一層進來，遇到工具頁回扣分類責任的段落時，再回到分類語言那一層補那一類的定義。&lt;/p>
&lt;h2 id="依讀者目的排的閱讀路線從沒部署過服務到規模成長">依讀者目的排的閱讀路線：從沒部署過服務到規模成長&lt;/h2>
&lt;p>下表每一列是一種讀者目的。建議順序欄的箭頭是閱讀先後，讀完那一欄說的是走完之後能做出的判斷。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>路線&lt;/th>
 &lt;th>適合讀者&lt;/th>
 &lt;th>建議順序&lt;/th>
 &lt;th>讀完能做什麼&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>沒部署過服務&lt;/td>
 &lt;td>會寫程式、但沒把服務上線過&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/devops/going-live/" data-link-title="服務上線：部署、主機形態、域名與 HTTPS、十二要素、自架或託管、備份與還原" data-link-desc="第一次把在本機執行的服務放上網際網路，要決定部署方式、主機形態、域名與 HTTPS、照十二要素分離設定與程式碼、資料庫自架或託管，以及備份還原怎麼做時讀">服務上線：部署、主機形態、域名與 HTTPS、十二要素、自架或託管、備份與還原&lt;/a>（部署是什麼 → 主機形態 → 域名與 HTTPS → 自架或託管 → 備份）→ 回到&lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/" data-link-title="模組零：後端服務選型" data-link-desc="從需求類型判斷資料庫、快取、訊息佇列、觀測與部署平台的選型方向">後端服務選型模組&lt;/a>做選型&lt;/td>
 &lt;td>能把一個服務從本機推上線，並知道接著補哪一塊&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>系統心智模型&lt;/td>
 &lt;td>想理解後端服務怎麼分工&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/delivery-mode-selection/" data-link-title="0.21 交付形態選型：從全託管到自建的光譜與邊界" data-link-desc="在進入資料庫、快取與部署選型之前、先判斷服務該用託管平台（Wix / Shopify / Google Sites）、辦公生態自動化（Apps Script）、BaaS（Firebase）、半託管 CMS（WordPress）還是自建、並為日後遷往自建保留可遷出路徑">0.21 交付形態選型：從全託管到自建的光譜與邊界&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/" data-link-title="模組零：後端服務選型" data-link-desc="從需求類型判斷資料庫、快取、訊息佇列、觀測與部署平台的選型方向">後端服務選型模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">前置知識卡片&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">資料庫&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">快取&lt;/a>與&lt;a href="https://tarrragon.github.io/blog/backend/03-message-queue/" data-link-title="模組三：訊息佇列與事件傳遞" data-link-desc="整理 durable queue、broker、retry、outbox 與 idempotency 的後端實務">訊息佇列&lt;/a>三個模組的共同觀念 → &lt;a href="https://tarrragon.github.io/blog/backend/10-system-evolution/service-decomposition-boundaries/" data-link-title="10.1 服務拆分與邊界判讀" data-link-desc="整理 monolith vs microservice 取捨、服務邊界判讀訊號、拆分時機與回退路徑">10.1 服務拆分與邊界判讀&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/cloud-vendor-capability-mapping/" data-link-title="0.19 雲端服務對照地圖（AWS / GCP / Azure）" data-link-desc="把後端能力分類對照到 AWS / GCP / Azure 的具體服務名稱、保留跨雲遷移與選型差異的判讀重點">0.19 雲端服務對照地圖（AWS / GCP / Azure）&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/scaling-axes/" data-link-title="9.13 擴展軸與 Stateless 前提" data-link-desc="整理垂直 / 水平擴展取捨、stateless vs stateful 前提、auto scaling 操作模型與兩種擴展的 hidden cost">9.13 擴展軸與 Stateless 前提&lt;/a>&lt;/td>
 &lt;td>能把需求分類成正式狀態、暫存副本、非同步交接、觀測、部署或可靠性問題，並用服務邊界與擴展軸描述系統往哪裡走&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>API 到資料流&lt;/td>
 &lt;td>想設計 API 的對外契約，以及它背後的資料與事件流程&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/" data-link-title="模組十一：API 設計與對外契約" data-link-desc="整理 API 風格選型、資源建模、錯誤模型、版本與相容策略、冪等與對外流量語意的設計判斷標準；主流做法與各流派的深度論證分層收錄">API 設計與對外契約模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">資料庫與持久化模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&amp;#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 應用層查詢反模式與 Query 預算&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">快取與 Redis 模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/03-message-queue/" data-link-title="模組三：訊息佇列與事件傳遞" data-link-desc="整理 durable queue、broker、retry、outbox 與 idempotency 的後端實務">訊息佇列與事件傳遞模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台模組&lt;/a>的證據整理 → &lt;a href="https://tarrragon.github.io/blog/backend/01-database/production-slow-log-loop/" data-link-title="1.14 Production Slow Log Closed Loop" data-link-desc="把 production slow log 從『偶爾看一下』變成『定期審視 &amp;#43; PR review 整合 &amp;#43; regression 偵測』的閉環、補 1.13 反模式清單後的操作層">1.14 Production Slow Log Closed Loop&lt;/a>&lt;/td>
 &lt;td>能說明一次 checkout 怎麼跨資料庫、快取、佇列與證據整理，並在上線前用 query 預算與 slow log 攔下查詢反模式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Production 操作&lt;/td>
 &lt;td>想學上線、觀測、驗證與事故的閉環&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">部署平台與網路入口模組&lt;/a>（含 &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/edge-cdn-static-distribution/" data-link-title="5.9 邊緣分發與靜態資源（CDN / Origin Protection）" data-link-desc="整理 CDN 與 edge cache 在部署平台中的責任邊界、origin protection、purge 與 invalidation 策略">5.9 邊緣分發與靜態資源（CDN / Origin Protection）&lt;/a>）→ &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤模組&lt;/a>&lt;/td>
 &lt;td>能把 release、alert、gate、邊緣層的 origin protection、事故決策紀錄與事後回寫串成操作閉環&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>資安與資料保護&lt;/td>
 &lt;td>想理解權限、秘密、資料、偵測與回應&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">資安與資料保護模組&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台模組&lt;/a>的稽核證據 → &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程模組&lt;/a>的控制驗證 → &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/security-vs-operational-incident/" data-link-title="8.17 Security Incident vs Operational Incident 分流" data-link-desc="把資安事故跟可用性事故的 IR 流程分支點明確化">8.17 Security Incident vs Operational Incident 分流&lt;/a> → &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/compromised-build-host-eradication/" data-link-title="8.25 建置主機被入侵後的根除與重建：清查範圍、重建來源、存取憑證撤銷與輪替的先後、多波攻擊的監看、宣告已掌握狀況的條件與承諾賠償的時機" data-link-desc="一台持有程式碼與正式環境存取權的主機（以 CI 建置主機為例）被入侵之後，清查範圍怎麼從它手上的存取憑證、讀寫得到的資料與網路連得到的系統推出來，重建時哪些來源仍可信，存取憑證撤銷與發新的先後，攻擊者回來時的監看與它的結束條件，以及對外宣告已掌握狀況、通報與承諾賠償各要先具備哪些事實">8.25 建置主機被入侵後的根除與重建&lt;/a>&lt;/td>
 &lt;td>能從身份、資料、入口、秘密與稽核證據判讀控制面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>工具比較與遷移&lt;/td>
 &lt;td>已懂分類、要比較工具或規劃遷移&lt;/td>
 &lt;td>對應模組的主章 → &lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/cloud-vendor-capability-mapping/" data-link-title="0.19 雲端服務對照地圖（AWS / GCP / Azure）" data-link-desc="把後端能力分類對照到 AWS / GCP / Azure 的具體服務名稱、保留跨雲遷移與選型差異的判讀重點">0.19 雲端服務對照地圖（AWS / GCP / Azure）&lt;/a> → 該模組的 &lt;code>vendors/&lt;/code> → 遷移操作手冊（含 &lt;a href="https://tarrragon.github.io/blog/backend/10-system-evolution/service-decomposition-execution-runbook/" data-link-title="10.2 服務拆分執行 Runbook（Strangler Fig / 雙寫期 / 切流 / 回退）" data-link-desc="10.1 決定該拆之後、實際怎麼動手拆 — Strangler Fig pattern、雙寫期管理、切流策略、回退條件設計">10.2 服務拆分執行 Runbook（Strangler Fig / 雙寫期 / 切流 / 回退）&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/10-system-evolution/managed-platform-exit/" data-link-title="10.3 託管形態遷出：資產線盤點與並行期執行" data-link-desc="0.21 升級自建 tripwire 觸發後的執行劇本 — 把遷出拆成資料、身分、流量、整合各自的可攜性與斷點、設計舊平台與新系統的並行期與回切窗口、用部分遷出作為中繼形態">10.3 託管形態遷出：資產線盤點與並行期執行&lt;/a>）→ 案例（含 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/production-slow-log-loop/" data-link-title="1.14 Production Slow Log Closed Loop" data-link-desc="把 production slow log 從『偶爾看一下』變成『定期審視 &amp;#43; PR review 整合 &amp;#43; regression 偵測』的閉環、補 1.13 反模式清單後的操作層">1.14 Production Slow Log Closed Loop&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/connection-pool-amplification/" data-link-title="9.14 連線池放大解法（PgBouncer / RDS Proxy / ProxySQL）" data-link-desc="水平擴展應用層時 DB 連線池放大問題的具體解法、connection pooler 三大選項對比、解 9.13 提出但未深入的隱性成本">9.14 連線池放大解法（PgBouncer / RDS Proxy / ProxySQL）&lt;/a>）&lt;/td>
 &lt;td>能先判斷分類責任，再對照雲端能力、比較具體服務、操作成本與遷移風險&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>規模成長&lt;/td>
 &lt;td>已經做出能跑的服務，要學讓它撐住&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/scale-growth-walls/" data-link-title="服務規模成長的閱讀路線：架構撞到限制的訊號、要補的能力與對應章節" data-link-desc="一個服務從一臺機器演進到多區域多服務的過程裡架構撞到限制的訊號、每個訊號對應要補的能力與章節，以及入門影片常用的比喻各對應到哪一篇">服務規模成長的閱讀路線：架構撞到限制的訊號、要補的能力與對應章節&lt;/a>，那一篇有完整的閱讀順序與各訊號對應的章節&lt;/td>
 &lt;td>能描述一個服務從一臺機器演進到多區域多服務的過程、每一段會撞上什麼、要先補哪一塊能力&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>各條路線共用同一組後端詞彙，所以可以只走一條，也可以從系統心智模型那一條開始，再依工作需求轉進其他路線。表格裡帶編號的連結是單篇文章，只寫模組名的連結是整個模組。&lt;/p></description><content:encoded><![CDATA[<p>這篇整理 Backend 教材各模組之間的閱讀順序：教材分成哪幾層、依讀者手上的目的各走哪一條路線，以及一條把各模組串起來的 checkout 流程。範圍是模組之間的順序，每個模組內部的章節順序在該模組的列表頁。</p>
<h2 id="backend-教材的分層分類語言服務路徑與具體工具各自回答的問題">Backend 教材的分層：分類語言、服務路徑與具體工具各自回答的問題</h2>
<p>Backend 教材把後端服務能力分成三層來教。<strong>分類語言</strong>讓讀者知道資料、快取、事件、觀測、部署、驗證、資安、事故與容量各自承擔什麼責任；<strong>服務路徑</strong>用一條具體業務流程，演練這些能力之間怎麼交接；<strong>具體工具</strong>討論 PostgreSQL、Redis、Kafka、Kubernetes、PagerDuty、k6 這些服務落到真實操作時的能力、成本與遷移。</p>
<table>
  <thead>
      <tr>
          <th>層</th>
          <th>回答的問題</th>
          <th>從哪裡讀起</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>分類語言</td>
          <td>這是哪一類問題</td>
          <td><a href="/blog/backend/00-service-selection/" data-link-title="模組零：後端服務選型" data-link-desc="從需求類型判斷資料庫、快取、訊息佇列、觀測與部署平台的選型方向">後端服務選型模組</a>與<a href="/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">前置知識卡片</a></td>
      </tr>
      <tr>
          <td>服務路徑</td>
          <td>一個服務流程怎麼把多個能力接起來</td>
          <td>資料庫到容量規劃各模組的主章，加上本篇下方的 checkout 流程</td>
      </tr>
      <tr>
          <td>具體工具</td>
          <td>要選哪個工具、怎麼遷移</td>
          <td>各模組的 <code>vendors/</code> 子目錄；案例庫在<a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">資安與資料保護模組</a>與<a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置在延遲目標內處理得了多少請求、要加多少機器』變成可量化、可驗證、可改進的工程流程">效能工程與容量規劃模組</a></td>
      </tr>
  </tbody>
</table>
<p>三層的順序是理解的順序：分類語言先建立，服務路徑才有詞彙可以描述交接，具體工具的比較才有一個分類責任可以回扣。已經懂分類、只想比較工具的讀者可以直接從具體工具那一層進來，遇到工具頁回扣分類責任的段落時，再回到分類語言那一層補那一類的定義。</p>
<h2 id="依讀者目的排的閱讀路線從沒部署過服務到規模成長">依讀者目的排的閱讀路線：從沒部署過服務到規模成長</h2>
<p>下表每一列是一種讀者目的。建議順序欄的箭頭是閱讀先後，讀完那一欄說的是走完之後能做出的判斷。</p>
<table>
  <thead>
      <tr>
          <th>路線</th>
          <th>適合讀者</th>
          <th>建議順序</th>
          <th>讀完能做什麼</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>沒部署過服務</td>
          <td>會寫程式、但沒把服務上線過</td>
          <td><a href="/blog/devops/going-live/" data-link-title="服務上線：部署、主機形態、域名與 HTTPS、十二要素、自架或託管、備份與還原" data-link-desc="第一次把在本機執行的服務放上網際網路，要決定部署方式、主機形態、域名與 HTTPS、照十二要素分離設定與程式碼、資料庫自架或託管，以及備份還原怎麼做時讀">服務上線：部署、主機形態、域名與 HTTPS、十二要素、自架或託管、備份與還原</a>（部署是什麼 → 主機形態 → 域名與 HTTPS → 自架或託管 → 備份）→ 回到<a href="/blog/backend/00-service-selection/" data-link-title="模組零：後端服務選型" data-link-desc="從需求類型判斷資料庫、快取、訊息佇列、觀測與部署平台的選型方向">後端服務選型模組</a>做選型</td>
          <td>能把一個服務從本機推上線，並知道接著補哪一塊</td>
      </tr>
      <tr>
          <td>系統心智模型</td>
          <td>想理解後端服務怎麼分工</td>
          <td><a href="/blog/backend/00-service-selection/delivery-mode-selection/" data-link-title="0.21 交付形態選型：從全託管到自建的光譜與邊界" data-link-desc="在進入資料庫、快取與部署選型之前、先判斷服務該用託管平台（Wix / Shopify / Google Sites）、辦公生態自動化（Apps Script）、BaaS（Firebase）、半託管 CMS（WordPress）還是自建、並為日後遷往自建保留可遷出路徑">0.21 交付形態選型：從全託管到自建的光譜與邊界</a> → <a href="/blog/backend/00-service-selection/" data-link-title="模組零：後端服務選型" data-link-desc="從需求類型判斷資料庫、快取、訊息佇列、觀測與部署平台的選型方向">後端服務選型模組</a> → <a href="/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">前置知識卡片</a> → <a href="/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">資料庫</a>、<a href="/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">快取</a>與<a href="/blog/backend/03-message-queue/" data-link-title="模組三：訊息佇列與事件傳遞" data-link-desc="整理 durable queue、broker、retry、outbox 與 idempotency 的後端實務">訊息佇列</a>三個模組的共同觀念 → <a href="/blog/backend/10-system-evolution/service-decomposition-boundaries/" data-link-title="10.1 服務拆分與邊界判讀" data-link-desc="整理 monolith vs microservice 取捨、服務邊界判讀訊號、拆分時機與回退路徑">10.1 服務拆分與邊界判讀</a>、<a href="/blog/backend/00-service-selection/cloud-vendor-capability-mapping/" data-link-title="0.19 雲端服務對照地圖（AWS / GCP / Azure）" data-link-desc="把後端能力分類對照到 AWS / GCP / Azure 的具體服務名稱、保留跨雲遷移與選型差異的判讀重點">0.19 雲端服務對照地圖（AWS / GCP / Azure）</a>、<a href="/blog/backend/09-performance-capacity/scaling-axes/" data-link-title="9.13 擴展軸與 Stateless 前提" data-link-desc="整理垂直 / 水平擴展取捨、stateless vs stateful 前提、auto scaling 操作模型與兩種擴展的 hidden cost">9.13 擴展軸與 Stateless 前提</a></td>
          <td>能把需求分類成正式狀態、暫存副本、非同步交接、觀測、部署或可靠性問題，並用服務邊界與擴展軸描述系統往哪裡走</td>
      </tr>
      <tr>
          <td>API 到資料流</td>
          <td>想設計 API 的對外契約，以及它背後的資料與事件流程</td>
          <td><a href="/blog/backend/11-api-design/" data-link-title="模組十一：API 設計與對外契約" data-link-desc="整理 API 風格選型、資源建模、錯誤模型、版本與相容策略、冪等與對外流量語意的設計判斷標準；主流做法與各流派的深度論證分層收錄">API 設計與對外契約模組</a> → <a href="/blog/backend/01-database/" data-link-title="模組一：資料庫與持久化" data-link-desc="整理 SQL、transaction、migration 與 repository adapter 的後端實務">資料庫與持久化模組</a> → <a href="/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 應用層查詢反模式與 Query 預算</a> → <a href="/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">快取與 Redis 模組</a> → <a href="/blog/backend/03-message-queue/" data-link-title="模組三：訊息佇列與事件傳遞" data-link-desc="整理 durable queue、broker、retry、outbox 與 idempotency 的後端實務">訊息佇列與事件傳遞模組</a> → <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台模組</a>的證據整理 → <a href="/blog/backend/01-database/production-slow-log-loop/" data-link-title="1.14 Production Slow Log Closed Loop" data-link-desc="把 production slow log 從『偶爾看一下』變成『定期審視 &#43; PR review 整合 &#43; regression 偵測』的閉環、補 1.13 反模式清單後的操作層">1.14 Production Slow Log Closed Loop</a></td>
          <td>能說明一次 checkout 怎麼跨資料庫、快取、佇列與證據整理，並在上線前用 query 預算與 slow log 攔下查詢反模式</td>
      </tr>
      <tr>
          <td>Production 操作</td>
          <td>想學上線、觀測、驗證與事故的閉環</td>
          <td><a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台模組</a> → <a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">部署平台與網路入口模組</a>（含 <a href="/blog/backend/05-deployment-platform/edge-cdn-static-distribution/" data-link-title="5.9 邊緣分發與靜態資源（CDN / Origin Protection）" data-link-desc="整理 CDN 與 edge cache 在部署平台中的責任邊界、origin protection、purge 與 invalidation 策略">5.9 邊緣分發與靜態資源（CDN / Origin Protection）</a>）→ <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程模組</a> → <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤模組</a></td>
          <td>能把 release、alert、gate、邊緣層的 origin protection、事故決策紀錄與事後回寫串成操作閉環</td>
      </tr>
      <tr>
          <td>資安與資料保護</td>
          <td>想理解權限、秘密、資料、偵測與回應</td>
          <td><a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">資安與資料保護模組</a> → <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台模組</a>的稽核證據 → <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程模組</a>的控制驗證 → <a href="/blog/backend/08-incident-response/security-vs-operational-incident/" data-link-title="8.17 Security Incident vs Operational Incident 分流" data-link-desc="把資安事故跟可用性事故的 IR 流程分支點明確化">8.17 Security Incident vs Operational Incident 分流</a> → <a href="/blog/backend/08-incident-response/compromised-build-host-eradication/" data-link-title="8.25 建置主機被入侵後的根除與重建：清查範圍、重建來源、存取憑證撤銷與輪替的先後、多波攻擊的監看、宣告已掌握狀況的條件與承諾賠償的時機" data-link-desc="一台持有程式碼與正式環境存取權的主機（以 CI 建置主機為例）被入侵之後，清查範圍怎麼從它手上的存取憑證、讀寫得到的資料與網路連得到的系統推出來，重建時哪些來源仍可信，存取憑證撤銷與發新的先後，攻擊者回來時的監看與它的結束條件，以及對外宣告已掌握狀況、通報與承諾賠償各要先具備哪些事實">8.25 建置主機被入侵後的根除與重建</a></td>
          <td>能從身份、資料、入口、秘密與稽核證據判讀控制面</td>
      </tr>
      <tr>
          <td>工具比較與遷移</td>
          <td>已懂分類、要比較工具或規劃遷移</td>
          <td>對應模組的主章 → <a href="/blog/backend/00-service-selection/cloud-vendor-capability-mapping/" data-link-title="0.19 雲端服務對照地圖（AWS / GCP / Azure）" data-link-desc="把後端能力分類對照到 AWS / GCP / Azure 的具體服務名稱、保留跨雲遷移與選型差異的判讀重點">0.19 雲端服務對照地圖（AWS / GCP / Azure）</a> → 該模組的 <code>vendors/</code> → 遷移操作手冊（含 <a href="/blog/backend/10-system-evolution/service-decomposition-execution-runbook/" data-link-title="10.2 服務拆分執行 Runbook（Strangler Fig / 雙寫期 / 切流 / 回退）" data-link-desc="10.1 決定該拆之後、實際怎麼動手拆 — Strangler Fig pattern、雙寫期管理、切流策略、回退條件設計">10.2 服務拆分執行 Runbook（Strangler Fig / 雙寫期 / 切流 / 回退）</a>、<a href="/blog/backend/10-system-evolution/managed-platform-exit/" data-link-title="10.3 託管形態遷出：資產線盤點與並行期執行" data-link-desc="0.21 升級自建 tripwire 觸發後的執行劇本 — 把遷出拆成資料、身分、流量、整合各自的可攜性與斷點、設計舊平台與新系統的並行期與回切窗口、用部分遷出作為中繼形態">10.3 託管形態遷出：資產線盤點與並行期執行</a>）→ 案例（含 <a href="/blog/backend/01-database/production-slow-log-loop/" data-link-title="1.14 Production Slow Log Closed Loop" data-link-desc="把 production slow log 從『偶爾看一下』變成『定期審視 &#43; PR review 整合 &#43; regression 偵測』的閉環、補 1.13 反模式清單後的操作層">1.14 Production Slow Log Closed Loop</a>、<a href="/blog/backend/09-performance-capacity/connection-pool-amplification/" data-link-title="9.14 連線池放大解法（PgBouncer / RDS Proxy / ProxySQL）" data-link-desc="水平擴展應用層時 DB 連線池放大問題的具體解法、connection pooler 三大選項對比、解 9.13 提出但未深入的隱性成本">9.14 連線池放大解法（PgBouncer / RDS Proxy / ProxySQL）</a>）</td>
          <td>能先判斷分類責任，再對照雲端能力、比較具體服務、操作成本與遷移風險</td>
      </tr>
      <tr>
          <td>規模成長</td>
          <td>已經做出能跑的服務，要學讓它撐住</td>
          <td><a href="/blog/backend/scale-growth-walls/" data-link-title="服務規模成長的閱讀路線：架構撞到限制的訊號、要補的能力與對應章節" data-link-desc="一個服務從一臺機器演進到多區域多服務的過程裡架構撞到限制的訊號、每個訊號對應要補的能力與章節，以及入門影片常用的比喻各對應到哪一篇">服務規模成長的閱讀路線：架構撞到限制的訊號、要補的能力與對應章節</a>，那一篇有完整的閱讀順序與各訊號對應的章節</td>
          <td>能描述一個服務從一臺機器演進到多區域多服務的過程、每一段會撞上什麼、要先補哪一塊能力</td>
      </tr>
  </tbody>
</table>
<p>各條路線共用同一組後端詞彙，所以可以只走一條，也可以從系統心智模型那一條開始，再依工作需求轉進其他路線。表格裡帶編號的連結是單篇文章，只寫模組名的連結是整個模組。</p>
<h2 id="貫穿各模組的-checkout-範例流程與各模組處理的問題">貫穿各模組的 checkout 範例流程與各模組處理的問題</h2>
<p>各模組可以獨立閱讀，而讀者需要一個共同的場景，才看得出某一章處理的是整個服務的哪一段。Backend 教材用一條簡化的 checkout 流程當這個場景（這個 checkout 與另外三個範例系統的功能、需求與規模假設見 <a href="/blog/backend/case-systems/" data-link-title="Backend 範例系統：短網址、售票搶購、電商 checkout 與交易撮合的功能、需求與規模假設" data-link-desc="Backend 教材幾篇以具體系統走完整條請求路徑的文章共用的四個範例系統：每個系統提供的功能與請求種類、不能讓步與可以讓步的需求、規模假設與起點架構，以及四個系統在成功請求的總數是否有上限、延遲是否改變請求的價值、同一個請求被處理兩次的後果上的差別">Backend 範例系統</a>）：使用者建立訂單、付款服務回應、商品或價格資料被快取、事件送到下游的通知與報表、服務上線時產出觀測與驗證證據、事故發生時留下決策紀錄並回寫改善。</p>
<p>這條流程在演進中會遇到下面這些問題，每一個問題需要好幾個模組一起處理：</p>
<table>
  <thead>
      <tr>
          <th>問題</th>
          <th>一起處理的模組</th>
          <th>產物</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>訂單表新增付款狀態欄位</td>
          <td>資料庫、可觀測性、事故處理</td>
          <td>遷移計畫、驗證查詢、出事時的決策路徑</td>
      </tr>
      <tr>
          <td>商品價格快取失效，回源流量要保護</td>
          <td>快取、可觀測性、可靠性驗證</td>
          <td>快取證據、origin protection gate、預熱計畫</td>
      </tr>
      <tr>
          <td>訂單事件的 consumer 失敗，要重播</td>
          <td>訊息佇列、可靠性驗證、事故處理</td>
          <td>冪等證據、死信佇列的處理方式、重播 runbook</td>
      </tr>
      <tr>
          <td>Checkout 服務發版</td>
          <td>部署平台、可觀測性、事故處理</td>
          <td>發版計畫、canary 證據、drain 訊號、回滾條件</td>
      </tr>
      <tr>
          <td>付款供應商的逾時設定變更</td>
          <td>可靠性驗證、可觀測性、效能與容量</td>
          <td>release gate、SLO 證據、容量基準</td>
      </tr>
      <tr>
          <td>Webhook 的 secret 輪替</td>
          <td>資安與資料保護、可觀測性、事故處理</td>
          <td>影響範圍圖、稽核證據、回滾視窗</td>
      </tr>
      <tr>
          <td>限時搶購的高峰準備</td>
          <td>效能與容量、快取、訊息佇列、可靠性驗證</td>
          <td>負載模型、飽和證據、佇列與快取的容量 gate</td>
      </tr>
  </tbody>
</table>
<p>讀到資料庫模組時，它處理的是訂單表欄位演進那一段；讀到訊息佇列模組時，它處理的是訂單事件在程序之間的交接；讀到事故處理模組時，它收斂的是欄位演進、事件重播、發版與 secret 輪替這幾個問題出事之後的決策與回寫。</p>
]]></content:encoded></item><item><title>SQL.0 一段查詢從需求到上線的決定：追問需求、設定判斷標準、選寫法、補前提、量代價、對邊界與交付</title><link>https://tarrragon.github.io/blog/backend/01-database/sql/decisions-from-requirement-to-shipping/</link><pubDate>Tue, 22 Sep 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/sql/decisions-from-requirement-to-shipping/</guid><description>&lt;p>這篇整理一段 SQL 查詢從業務的一句話走到上線之間要做的決定——追問需求、設定判斷標準、選寫法、補前提、量代價、對邊界、交付——每一個決定要決定什麼、決錯之後看到什麼症狀，以及它背後的機制在哪一篇展開。範圍從拿到需求的那一刻開始，到查詢上線、交給下一個環境為止。&lt;/p>
&lt;h2 id="各個決定的內容決錯之後的症狀與機制所在的篇章">各個決定的內容、決錯之後的症狀與機制所在的篇章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>決定&lt;/th>
 &lt;th>要決定的&lt;/th>
 &lt;th>決錯之後的症狀&lt;/th>
 &lt;th>機制在&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>追問需求&lt;/td>
 &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>改完查詢不知道有沒有改好&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/well-formed-is-not-correct/" data-link-title="SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法" data-link-desc="合法與問對了分別由誰負責、答案錯掉時引擎為什麼不報錯，以及靜態分析、約束與反例資料各自判得出哪些錯">SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>選寫法&lt;/td>
 &lt;td>除了判斷有沒有，還要不要用到被比對那張表的欄位&lt;/td>
 &lt;td>列數不是預期的，而後面的加總跟著錯&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/in-exists-join/" data-link-title="SQL.8 IN、EXISTS 與 JOIN：列數、可取用的欄位與 NULL 的處理" data-link-desc="同一個「找出有對應資料的那些」的問題用成員判斷、存在性判斷與連接各寫一次的結果，選用的判斷標準，以及子查詢含 NULL 時 NOT IN 與 NOT EXISTS 的分岔">SQL.8 IN、EXISTS 與 JOIN：列數、可取用的欄位與 NULL 的處理&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/table-occurrence-and-alias/" data-link-title="SQL.7 表的出現與別名：表名的指稱、別名的作用範圍與自連接" data-link-desc="FROM 裡的表名指的是什麼、同名的兩次出現為什麼被引擎擋下、自連接的前後配對、同組比較與層級關係，以及別名取名的依據">SQL.7 表的出現與別名：表名的指稱、別名的作用範圍與自連接&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/window-keeps-rows-grouping-collapses/" data-link-title="SQL.10 分組與視窗函數：各自的產出、選用的依據與 LAG、LEAD 的相鄰列" data-link-desc="GROUP BY 與視窗函數算完之後原本的列還在不在、依結果的一列代表什麼選用哪一個、視窗函數的值怎麼拿來當篩選條件，以及 LAG 取相鄰列時分區、排序鍵與距離條件各自要確認的事">SQL.10 分組與視窗函數：各自的產出、選用的依據與 LAG、LEAD 的相鄰列&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>補前提&lt;/td>
 &lt;td>這段寫法默默假設了什麼&lt;/td>
 &lt;td>資料長成某個樣子之後答案就錯，而不報錯&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/join-changes-rows-and-nulls/" data-link-title="SQL.6 連接之後的列數與空缺：列數膨脹、外連接補的 NULL 與三值邏輯" data-link-desc="連接讓聚合多算的機制與計數、加總、平均、中位數各自的修法，以及外連接補出來的 NULL 在 count、等號比較、NOT IN 與 NOT EXISTS 上的行為">SQL.6 連接之後的列數與空缺：列數膨脹、外連接補的 NULL 與三值邏輯&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/pagination-needs-a-total-order/" data-link-title="SQL.12 分頁的排序鍵與接續標記：同分的列、全序與 OFFSET 的代價" data-link-desc="每一頁各查一次時同分的列與翻頁途中的寫入怎麼造成重複與遺漏、排序鍵補到兩兩可分的條件、展開式與列值式的接續標記條件在排序方向上的差別，以及 OFFSET 隨頁數成長的代價">SQL.12 分頁的排序鍵與接續標記：同分的列、全序與 OFFSET 的代價&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>量代價&lt;/td>
 &lt;td>這段要跑多久、跑幾次&lt;/td>
 &lt;td>開發時很快，正式資料上拖垮請求&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/cost-lives-in-the-plan/" data-link-title="SQL.17 查詢的代價：資料分布、索引與寫法各自的影響" data-link-desc="找重複值的分組、自連接與 EXISTS 在有無索引與不同重複次數下的實測時間與計畫，以及比較查詢快慢時要補上的資料量、分布、索引與統計資訊">SQL.17 查詢的代價：資料分布、索引與寫法各自的影響&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/knowledge-cards/sargable/" data-link-title="Sargable（可走索引的條件形狀）" data-link-desc="索引存在而計畫仍走全表掃描時，查條件的寫法有沒有擋住那個索引">Sargable&lt;/a> 條件的形狀決定索引用不用得上&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>對邊界&lt;/td>
 &lt;td>需求沒說而資料會有的那些&lt;/td>
 &lt;td>平常都對，特定幾筆資料上靜默算錯&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/relations-have-no-order/" data-link-title="SQL.11 查詢結果的列序：沒有 ORDER BY 時的來源與 ORDER BY 的規定範圍" data-link-desc="同一段查詢在建索引前後回不同順序的機制、ORDER BY 在求值順序的位置，以及空值落在哪一端各家的差別">SQL.11 查詢結果的列序：沒有 ORDER BY 時的來源與 ORDER BY 的規定範圍&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/well-formed-is-not-correct/" data-link-title="SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法" data-link-desc="合法與問對了分別由誰負責、答案錯掉時引擎為什麼不報錯，以及靜態分析、約束與反例資料各自判得出哪些錯">SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>交付&lt;/td>
 &lt;td>換引擎、換資料量、換讀的人之後還成不成立&lt;/td>
 &lt;td>會報錯的差異搬家當天就浮現，不報錯的要等某一筆資料才現形或始終不出聲&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/engine-leniency-and-portability/" data-link-title="SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機" data-link-desc="SQLite、DuckDB、PostgreSQL 與 MySQL 在未分組欄位、WHERE 用別名、同表未取別名、字串比對與空值排序上的實測對照，差異從當場報錯到完全靜默的分級，以及依目標組態數決定哪幾級差異在寫的當下處理">SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機&lt;/a> 管換引擎、&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/declared-intent-vs-behaviour/" data-link-title="SQL.20 關鍵字的宣告與引擎的行為：宣告落空的形態與查證" data-link-desc="LEFT JOIN、CROSS JOIN、DISTINCT、COALESCE 與表的別名各自對讀的人宣告了什麼、宣告落空時下一個修改的人付出什麼代價，以及把關鍵字換成最弱的版本再跑一次的查證動作">SQL.20 關鍵字的宣告與引擎的行為：宣告落空的形態與查證&lt;/a> 管換讀的人&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>決錯之後的症狀有一個共同點：查詢照跑而答案不對。&lt;/strong> 只有兩個決定決錯時會當場出聲：量代價決錯時查詢慢得看得出來；交付時換了引擎，有一類差異第一次執行就報錯。&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/engine-leniency-and-portability/" data-link-title="SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機" data-link-desc="SQLite、DuckDB、PostgreSQL 與 MySQL 在未分組欄位、WHERE 用別名、同表未取別名、字串比對與空值排序上的實測對照，差異從當場報錯到完全靜默的分級，以及依目標組態數決定哪幾級差異在寫的當下處理">SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機&lt;/a> 把跨引擎的差異按發聲的位置分級，第一次執行就報錯的叫當場報錯，其餘的差異要麼等某一筆資料才報錯、要麼始終靜默。其餘的決定決錯時欄位正常、列數合理，沒有任何東西提示要去檢查。&lt;/p>
&lt;h2 id="最常被跳過的決定追問需求設定判斷標準與對邊界">最常被跳過的決定：追問需求、設定判斷標準與對邊界&lt;/h2>
&lt;p>&lt;strong>追問需求與設定判斷標準。&lt;/strong> 這兩個決定發生在打開編輯器之前，所以不留下任何痕跡——跳過了的查詢跟沒跳過的長得一模一樣。而跳過追問需求的代價是整段查詢答錯題，跳過設定判斷標準的代價是後面每一次修改都不知道有沒有改好。&lt;/p>
&lt;p>&lt;strong>對邊界。&lt;/strong> 一句需求描述的是資料正常時的樣子，而正式資料裡會出現提出需求的人沒想到、所以沒說的情形。以「哪幾天氣溫比前一天高」為例：同一個測站同一天有兩筆紀錄（感測器重送、補登、換設備之後兩套系統各寫一筆）、觀測日存的是哪一個時區的日期、三個月後才補進來的舊資料讓昨天的答案今天變了、同一個測站代號在某一天之後指向另一個地點。對邊界這個決定，就是在查詢寫好之後把這些情形逐一列出來，並決定每一種出現時查詢該回什麼。&lt;/p>
&lt;p>這份清單沒有地方可以查。需求裡沒有它，schema 也只記下有哪些欄位、各是什麼型別，不記下資料在現實中會怎麼偏離預期。列得出幾種，取決於看過多少這個領域的資料：看過感測器重送，才會想到同一天可能有兩筆；看過補登，才會想到歷史答案會變。所以這一步的能力靠經驗累積，每在正式資料上遇到一種新的情形，就把它加進這個領域的查詢的檢查清單。&lt;/p>
&lt;p>跳過它的症狀最難察覺：查詢在大部分資料上都對，只在碰到那幾筆特殊資料時算錯，而且不報錯。那幾筆往往要到有人拿結果去對帳，才被發現。&lt;/p>
&lt;h2 id="決定的前提建表時定下的表的形狀">決定的前提：建表時定下的表的形狀&lt;/h2>
&lt;p>從追問需求到交付，每一個決定都做在一組已經建好的表上，而表的形狀是&lt;strong>建表時的決定&lt;/strong>造出來的：欄位允不允許為空、鍵唯不唯一、長欄位放哪一張表、常一起取的資料切在幾張表、字串的比較規則寫在哪一層、外鍵生不生效。選寫法、補前提的時候，鍵唯不唯一、欄位允不允許為空已經定了；量代價的時候，長欄位放哪一張表、常一起取的資料切在幾張表也已經定了。這些建表時的決定替往後每一段查詢定了價——手上要交的是一份 schema 設計而不是一段查詢時，問題落在追問需求之前。以鍵唯不唯一為例：「哪幾天氣溫比前一天高」用自連接、視窗函數與相關子查詢三種寫法，共同押的前提，是同一個測站同一天只有一筆；表上有 &lt;code>UNIQUE (測站, 觀測日)&lt;/code> 時這個前提由資料庫保證，沒有的話每一段查詢都得自己處理重複，否則自連接會把一列配成多列。每一個決定各自替查詢定了什麼代價，實測在 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/design-decisions-price-every-query/" data-link-title="1.16 Schema 設計決定的查詢代價：可空性、排序鍵唯一性、表寬、一對多的切法、比較規則與外鍵執法" data-link-desc="建表時每一個決定有哪些選法、選了之後查詢要多做什麼、代價在什麼條件下才浮現、改回來要付的遷移成本，以及設計當下就能問的問題；輸出為 SQLite 與 PostgreSQL 的實測">backend 1.16 Schema 設計決定的查詢代價&lt;/a>。&lt;/p>
&lt;h2 id="在哪幾天氣溫比前一天高這句需求上做完全部決定的示範">在「哪幾天氣溫比前一天高」這句需求上做完全部決定的示範&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/sql/requirement-to-shipped-query/" data-link-title="SQL.22 查詢設計的完整示範：把「哪幾天氣溫比前一天高」從需求做到上線" data-link-desc="以「哪幾天氣溫比前一天高」為例，把一句業務的話追問成可驗收的資料問題，比較自連接、視窗函數與相關子查詢各自押的前提與實測代價，以及需求沒說而資料會有的重複紀錄、時區與補登">SQL.22 查詢設計的完整示範：把「哪幾天氣溫比前一天高」從需求做到上線&lt;/a> 拿「哪幾天氣溫比前一天高」這句需求從頭做完這些決定：把「前一天」追問成日曆上的前一天還是資料上的前一筆；用自連接、視窗函數與相關子查詢各寫一次，找出三者共同押的前提（同一個測站同一天只有一筆）；看漏掉相隔天數與漏掉分區的兩個錯誤版本：漏掉相隔天數的多報，漏掉分區的錯在哪個方向要看同一天兩個測站誰排在前面；把自連接再分成日期差與等值兩種寫法，在同一組資料上量這兩種自連接、視窗函數與相關子查詢的時間，差到三個量級，其中一種加了索引反而變慢；最後列出重複紀錄、時區、補登與測站搬家這幾種需求沒說的情形。&lt;/p></description><content:encoded><![CDATA[<p>這篇整理一段 SQL 查詢從業務的一句話走到上線之間要做的決定——追問需求、設定判斷標準、選寫法、補前提、量代價、對邊界、交付——每一個決定要決定什麼、決錯之後看到什麼症狀，以及它背後的機制在哪一篇展開。範圍從拿到需求的那一刻開始，到查詢上線、交給下一個環境為止。</p>
<h2 id="各個決定的內容決錯之後的症狀與機制所在的篇章">各個決定的內容、決錯之後的症狀與機制所在的篇章</h2>
<table>
  <thead>
      <tr>
          <th>決定</th>
          <th>要決定的</th>
          <th>決錯之後的症狀</th>
          <th>機制在</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>追問需求</td>
          <td>這句話真正問的是哪一個資料問題</td>
          <td>查詢寫對了，而它答的是另一題</td>
          <td>答案要去問提出需求的人</td>
      </tr>
      <tr>
          <td>設定判斷標準</td>
          <td>什麼樣的輸出算對</td>
          <td>改完查詢不知道有沒有改好</td>
          <td><a href="/blog/backend/01-database/sql/well-formed-is-not-correct/" data-link-title="SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法" data-link-desc="合法與問對了分別由誰負責、答案錯掉時引擎為什麼不報錯，以及靜態分析、約束與反例資料各自判得出哪些錯">SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法</a></td>
      </tr>
      <tr>
          <td>選寫法</td>
          <td>除了判斷有沒有，還要不要用到被比對那張表的欄位</td>
          <td>列數不是預期的，而後面的加總跟著錯</td>
          <td><a href="/blog/backend/01-database/sql/in-exists-join/" data-link-title="SQL.8 IN、EXISTS 與 JOIN：列數、可取用的欄位與 NULL 的處理" data-link-desc="同一個「找出有對應資料的那些」的問題用成員判斷、存在性判斷與連接各寫一次的結果，選用的判斷標準，以及子查詢含 NULL 時 NOT IN 與 NOT EXISTS 的分岔">SQL.8 IN、EXISTS 與 JOIN：列數、可取用的欄位與 NULL 的處理</a>、<a href="/blog/backend/01-database/sql/table-occurrence-and-alias/" data-link-title="SQL.7 表的出現與別名：表名的指稱、別名的作用範圍與自連接" data-link-desc="FROM 裡的表名指的是什麼、同名的兩次出現為什麼被引擎擋下、自連接的前後配對、同組比較與層級關係，以及別名取名的依據">SQL.7 表的出現與別名：表名的指稱、別名的作用範圍與自連接</a>、<a href="/blog/backend/01-database/sql/window-keeps-rows-grouping-collapses/" data-link-title="SQL.10 分組與視窗函數：各自的產出、選用的依據與 LAG、LEAD 的相鄰列" data-link-desc="GROUP BY 與視窗函數算完之後原本的列還在不在、依結果的一列代表什麼選用哪一個、視窗函數的值怎麼拿來當篩選條件，以及 LAG 取相鄰列時分區、排序鍵與距離條件各自要確認的事">SQL.10 分組與視窗函數：各自的產出、選用的依據與 LAG、LEAD 的相鄰列</a></td>
      </tr>
      <tr>
          <td>補前提</td>
          <td>這段寫法默默假設了什麼</td>
          <td>資料長成某個樣子之後答案就錯，而不報錯</td>
          <td><a href="/blog/backend/01-database/sql/join-changes-rows-and-nulls/" data-link-title="SQL.6 連接之後的列數與空缺：列數膨脹、外連接補的 NULL 與三值邏輯" data-link-desc="連接讓聚合多算的機制與計數、加總、平均、中位數各自的修法，以及外連接補出來的 NULL 在 count、等號比較、NOT IN 與 NOT EXISTS 上的行為">SQL.6 連接之後的列數與空缺：列數膨脹、外連接補的 NULL 與三值邏輯</a>、<a href="/blog/backend/01-database/sql/pagination-needs-a-total-order/" data-link-title="SQL.12 分頁的排序鍵與接續標記：同分的列、全序與 OFFSET 的代價" data-link-desc="每一頁各查一次時同分的列與翻頁途中的寫入怎麼造成重複與遺漏、排序鍵補到兩兩可分的條件、展開式與列值式的接續標記條件在排序方向上的差別，以及 OFFSET 隨頁數成長的代價">SQL.12 分頁的排序鍵與接續標記：同分的列、全序與 OFFSET 的代價</a></td>
      </tr>
      <tr>
          <td>量代價</td>
          <td>這段要跑多久、跑幾次</td>
          <td>開發時很快，正式資料上拖垮請求</td>
          <td><a href="/blog/backend/01-database/sql/cost-lives-in-the-plan/" data-link-title="SQL.17 查詢的代價：資料分布、索引與寫法各自的影響" data-link-desc="找重複值的分組、自連接與 EXISTS 在有無索引與不同重複次數下的實測時間與計畫，以及比較查詢快慢時要補上的資料量、分布、索引與統計資訊">SQL.17 查詢的代價：資料分布、索引與寫法各自的影響</a>、<a href="/blog/backend/01-database/sql/knowledge-cards/sargable/" data-link-title="Sargable（可走索引的條件形狀）" data-link-desc="索引存在而計畫仍走全表掃描時，查條件的寫法有沒有擋住那個索引">Sargable</a> 條件的形狀決定索引用不用得上</td>
      </tr>
      <tr>
          <td>對邊界</td>
          <td>需求沒說而資料會有的那些</td>
          <td>平常都對，特定幾筆資料上靜默算錯</td>
          <td><a href="/blog/backend/01-database/sql/relations-have-no-order/" data-link-title="SQL.11 查詢結果的列序：沒有 ORDER BY 時的來源與 ORDER BY 的規定範圍" data-link-desc="同一段查詢在建索引前後回不同順序的機制、ORDER BY 在求值順序的位置，以及空值落在哪一端各家的差別">SQL.11 查詢結果的列序：沒有 ORDER BY 時的來源與 ORDER BY 的規定範圍</a>、<a href="/blog/backend/01-database/sql/well-formed-is-not-correct/" data-link-title="SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法" data-link-desc="合法與問對了分別由誰負責、答案錯掉時引擎為什麼不報錯，以及靜態分析、約束與反例資料各自判得出哪些錯">SQL.13 查詢的合法性與答案的正確性：引擎檢查的範圍、答案錯掉的成因與查證方法</a></td>
      </tr>
      <tr>
          <td>交付</td>
          <td>換引擎、換資料量、換讀的人之後還成不成立</td>
          <td>會報錯的差異搬家當天就浮現，不報錯的要等某一筆資料才現形或始終不出聲</td>
          <td><a href="/blog/backend/01-database/sql/engine-leniency-and-portability/" data-link-title="SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機" data-link-desc="SQLite、DuckDB、PostgreSQL 與 MySQL 在未分組欄位、WHERE 用別名、同表未取別名、字串比對與空值排序上的實測對照，差異從當場報錯到完全靜默的分級，以及依目標組態數決定哪幾級差異在寫的當下處理">SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機</a> 管換引擎、<a href="/blog/backend/01-database/sql/declared-intent-vs-behaviour/" data-link-title="SQL.20 關鍵字的宣告與引擎的行為：宣告落空的形態與查證" data-link-desc="LEFT JOIN、CROSS JOIN、DISTINCT、COALESCE 與表的別名各自對讀的人宣告了什麼、宣告落空時下一個修改的人付出什麼代價，以及把關鍵字換成最弱的版本再跑一次的查證動作">SQL.20 關鍵字的宣告與引擎的行為：宣告落空的形態與查證</a> 管換讀的人</td>
      </tr>
  </tbody>
</table>
<p><strong>決錯之後的症狀有一個共同點：查詢照跑而答案不對。</strong> 只有兩個決定決錯時會當場出聲：量代價決錯時查詢慢得看得出來；交付時換了引擎，有一類差異第一次執行就報錯。<a href="/blog/backend/01-database/sql/engine-leniency-and-portability/" data-link-title="SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機" data-link-desc="SQLite、DuckDB、PostgreSQL 與 MySQL 在未分組欄位、WHERE 用別名、同表未取別名、字串比對與空值排序上的實測對照，差異從當場報錯到完全靜默的分級，以及依目標組態數決定哪幾級差異在寫的當下處理">SQL.19 引擎寬鬆度與可攜性：各家對同一組寫法的差異、分級與處理時機</a> 把跨引擎的差異按發聲的位置分級，第一次執行就報錯的叫當場報錯，其餘的差異要麼等某一筆資料才報錯、要麼始終靜默。其餘的決定決錯時欄位正常、列數合理，沒有任何東西提示要去檢查。</p>
<h2 id="最常被跳過的決定追問需求設定判斷標準與對邊界">最常被跳過的決定：追問需求、設定判斷標準與對邊界</h2>
<p><strong>追問需求與設定判斷標準。</strong> 這兩個決定發生在打開編輯器之前，所以不留下任何痕跡——跳過了的查詢跟沒跳過的長得一模一樣。而跳過追問需求的代價是整段查詢答錯題，跳過設定判斷標準的代價是後面每一次修改都不知道有沒有改好。</p>
<p><strong>對邊界。</strong> 一句需求描述的是資料正常時的樣子，而正式資料裡會出現提出需求的人沒想到、所以沒說的情形。以「哪幾天氣溫比前一天高」為例：同一個測站同一天有兩筆紀錄（感測器重送、補登、換設備之後兩套系統各寫一筆）、觀測日存的是哪一個時區的日期、三個月後才補進來的舊資料讓昨天的答案今天變了、同一個測站代號在某一天之後指向另一個地點。對邊界這個決定，就是在查詢寫好之後把這些情形逐一列出來，並決定每一種出現時查詢該回什麼。</p>
<p>這份清單沒有地方可以查。需求裡沒有它，schema 也只記下有哪些欄位、各是什麼型別，不記下資料在現實中會怎麼偏離預期。列得出幾種，取決於看過多少這個領域的資料：看過感測器重送，才會想到同一天可能有兩筆；看過補登，才會想到歷史答案會變。所以這一步的能力靠經驗累積，每在正式資料上遇到一種新的情形，就把它加進這個領域的查詢的檢查清單。</p>
<p>跳過它的症狀最難察覺：查詢在大部分資料上都對，只在碰到那幾筆特殊資料時算錯，而且不報錯。那幾筆往往要到有人拿結果去對帳，才被發現。</p>
<h2 id="決定的前提建表時定下的表的形狀">決定的前提：建表時定下的表的形狀</h2>
<p>從追問需求到交付，每一個決定都做在一組已經建好的表上，而表的形狀是<strong>建表時的決定</strong>造出來的：欄位允不允許為空、鍵唯不唯一、長欄位放哪一張表、常一起取的資料切在幾張表、字串的比較規則寫在哪一層、外鍵生不生效。選寫法、補前提的時候，鍵唯不唯一、欄位允不允許為空已經定了；量代價的時候，長欄位放哪一張表、常一起取的資料切在幾張表也已經定了。這些建表時的決定替往後每一段查詢定了價——手上要交的是一份 schema 設計而不是一段查詢時，問題落在追問需求之前。以鍵唯不唯一為例：「哪幾天氣溫比前一天高」用自連接、視窗函數與相關子查詢三種寫法，共同押的前提，是同一個測站同一天只有一筆；表上有 <code>UNIQUE (測站, 觀測日)</code> 時這個前提由資料庫保證，沒有的話每一段查詢都得自己處理重複，否則自連接會把一列配成多列。每一個決定各自替查詢定了什麼代價，實測在 <a href="/blog/backend/01-database/design-decisions-price-every-query/" data-link-title="1.16 Schema 設計決定的查詢代價：可空性、排序鍵唯一性、表寬、一對多的切法、比較規則與外鍵執法" data-link-desc="建表時每一個決定有哪些選法、選了之後查詢要多做什麼、代價在什麼條件下才浮現、改回來要付的遷移成本，以及設計當下就能問的問題；輸出為 SQLite 與 PostgreSQL 的實測">backend 1.16 Schema 設計決定的查詢代價</a>。</p>
<h2 id="在哪幾天氣溫比前一天高這句需求上做完全部決定的示範">在「哪幾天氣溫比前一天高」這句需求上做完全部決定的示範</h2>
<p><a href="/blog/backend/01-database/sql/requirement-to-shipped-query/" data-link-title="SQL.22 查詢設計的完整示範：把「哪幾天氣溫比前一天高」從需求做到上線" data-link-desc="以「哪幾天氣溫比前一天高」為例，把一句業務的話追問成可驗收的資料問題，比較自連接、視窗函數與相關子查詢各自押的前提與實測代價，以及需求沒說而資料會有的重複紀錄、時區與補登">SQL.22 查詢設計的完整示範：把「哪幾天氣溫比前一天高」從需求做到上線</a> 拿「哪幾天氣溫比前一天高」這句需求從頭做完這些決定：把「前一天」追問成日曆上的前一天還是資料上的前一筆；用自連接、視窗函數與相關子查詢各寫一次，找出三者共同押的前提（同一個測站同一天只有一筆）；看漏掉相隔天數與漏掉分區的兩個錯誤版本：漏掉相隔天數的多報，漏掉分區的錯在哪個方向要看同一天兩個測站誰排在前面；把自連接再分成日期差與等值兩種寫法，在同一組資料上量這兩種自連接、視窗函數與相關子查詢的時間，差到三個量級，其中一種加了索引反而變慢；最後列出重複紀錄、時區、補登與測站搬家這幾種需求沒說的情形。</p>
]]></content:encoded></item><item><title>Backend 範例系統：短網址、售票搶購、電商 checkout 與交易撮合的功能、需求與規模假設</title><link>https://tarrragon.github.io/blog/backend/case-systems/</link><pubDate>Thu, 15 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/case-systems/</guid><description>&lt;p>這篇定義四個範例系統：短網址、售票搶購、電商 checkout 與交易撮合，供幾篇沿著具體系統的請求路徑逐段分析的文章共用，目前有 &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/load-path-analysis/" data-link-title="9.0 請求路徑上的負載分析：一次一筆的處理、在它之前能回應的請求、多出來的請求怎麼處置、保護把負載轉到哪裡、重送與去重，以及保護元件與依賴故障時的處置" data-link-desc="沿著一個會改變狀態的請求從用戶端到資料庫的路徑分析負載時共用的幾個概念：必須一次只處理一筆的那件事與它的互斥範圍、需要經過它的請求裡結果事先已確定的比例、多出來的請求要排隊、拒絕還是請使用者重新確認、每一段保護把負載轉給了哪個元件（保護元件自己的狀態、連線數、輪詢、下游接收端、外部配額）、佇列與重按帶來的重送怎麼去重、保護元件故障或輸出錯的值時下游讀到什麼，以及依賴變慢、部分失效與恢復時的第二波湧入">9.0 請求路徑上的負載分析&lt;/a> 的舉例、&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/ticketing-load-path/" data-link-title="9.0.1 售票搶購的負載路徑：熱點列與連線池、CDN 合併回源、排隊放行的速率、資料庫認領與加入 Redis 閘門的條件、付款期限的條件更新" data-link-desc="以售票搶購為例，沿著一張票從座位圖到付款記帳的路徑，寫出全部請求更新同一個庫存計數時熱點列與連線池的耗盡順序，邊緣快取、排隊放行、資料庫逐張認領與加入 Redis 扣庫存各自把負載轉給了哪一段、自己出錯時由哪一段補救，以及成功請求的總數有上限、延遲不改變請求的價值、先到先得這幾個前提讓哪些做法可以簡化、哪些需求無法簡化">9.0.1 售票搶購的負載路徑&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/matching-engine-load-path/" data-link-title="9.0.2 交易撮合的負載路徑：閘道的帳戶限流與明確拒絕、依序日誌的複寫延遲、單一寫入者的撮合引擎、行情的合併送出與重放驗證容量" data-link-desc="以交易撮合為例，沿著一筆委託從入口閘道到成交回報的路徑，寫出撮合之前結果無法確定、處理順序決定成交、延遲改變請求的價值這些性質讓哪些負載保護不能用，閘道、依序日誌、單一寫入者的撮合引擎與行情發布各自先耗盡的資源與出錯時的補救，過載時明確拒絕超量委託與以重放驗證容量的做法，並與售票搶購對照">9.0.2 交易撮合的負載路徑&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/monitoring-inventory-single-host/" data-link-title="4.0.2 以監控鏈盤點單機短網址服務：各層現有的監控、缺口的性質與補法，以及監控鏈事前偵測不到的異常" data-link-desc="把一台主機上以 Docker Compose 跑的短網址服務現有的每一種監控放進監控鏈的各層，逐層判讀還開著的缺口屬於檢查範圍盲區、位置盲區、讀取缺口還是訊號失真，各自的補法，以及補上失效域以外的觀察者之前，主機整台停掉時沒有任何一層會通知的情形；另逐類查業務結果、資料正確性、安全、成本、到期事件與備份">4.0.2 以監控鏈盤點單機短網址服務&lt;/a> 、&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/monitoring-inventory-k8s-checkout/" data-link-title="4.0.3 以監控鏈盤點 Kubernetes 上的電商 checkout：叢集內的監控、託管資料庫與第三方金流各自的失效域，以及佇列消費端與外部依賴的缺口" data-link-desc="把一個跑在託管 Kubernetes 上、有訊息佇列消費端、託管資料庫與第三方金流的電商 checkout 現有的監控放進監控鏈的各層，逐層判讀缺口與補法；另寫付款服務的 readiness 納入金流業者時對付款回呼的影響、失效域從主機換成叢集與雲端帳號之後觀察者要放在哪裡，以及業務結果、對帳、到期事件、消費端、備份、安全、第三方依賴與成本的逐類盤點">4.0.3 以監控鏈盤點 Kubernetes 上的電商 checkout&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/monitoring-inventory-matching-engine/" data-link-title="4.0.4 以監控鏈盤點交易撮合系統：觀察者讀數字的位置、沒有新委託時分辨市場安靜與路徑停住、分段量測延遲，以及委託序號、帳戶部位的對帳與其他監控鏈事前偵測不到的異常" data-link-desc="把一個交易撮合系統的監控放進監控鏈的各層判讀缺口：撮合引擎單一執行緒時觀察者為什麼要從輸出日誌讀數字、沒有新委託時怎麼分辨市場安靜還是處理路徑停住、委託延遲怎麼依段量測與時鐘同步的前提、依序日誌多數節點與同一可用區的失效域，以及輸出序號的斷號、帳戶部位與行情資料的一致性這類監控鏈事前偵測不到的異常">4.0.4 以監控鏈盤點交易撮合系統&lt;/a>。那幾篇引用的請求量、票數這類規模數字是本篇的假設，用來讓例子有具體的量，不是任何真實服務的數字；&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/host-traffic-resource-monitoring/" data-link-title="4.26 單機容器服務的流量與資源監控：各層的訊號、Prometheus 與 exporter、監控所在層的盲區" data-link-desc="健康檢查之外的流量與資源監控：入口、應用程式、容器、主機與資料庫等服務內部各層的訊號從哪裡收、用哪一個 exporter，Prometheus 加 exporter、單一 agent、開箱即用工具與臨時查看指令的取捨，單次取樣與時間窗的差別，監控裝在虛擬機裡時看不到主機的 CPU 負載、看到的磁碟要分清是哪一顆，以及每接上一個來源的驗收與偵測容器停止的寫法；cAdvisor 與 node_exporter 在 OrbStack 上實測過">4.26 單機容器服務的流量與資源監控&lt;/a> 到 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/single-host-monitoring-outside-view/" data-link-title="4.28 單台主機的監控盲區與失效域以外的觀察者：主機停機、使用者路徑中斷與告警鏈中斷，心跳監看與外部探測各自回答的問題與位置，以及與內部探測並排的判讀" data-link-desc="服務與監控放在同一台主機時監控結構上看不到的故障：主機整台停掉、使用者到主機的路徑中斷、告警鏈停掉與通知送不出去；從失效域以外送出訊號的心跳監看與外部探測各自回答的問題與成立條件，觀察者由第三方服務、另一個失效域的主機（含兩台主機互相監看）或另一個業者的排程平台提供時的差別，以及心跳監看、外部探測與主機內部探測並排時的判讀">4.28 單台主機的監控盲區與失效域以外的觀察者&lt;/a> 也用短網址服務當例子，那幾篇的量測數字來自實際的開發環境，不在本篇的假設之內。&lt;/p>
&lt;p>同一個元件在不同需求下的設計不同。售票系統可以讓大多數請求在進資料庫之前就收到「售完」，交易撮合每一筆被接受的委託都要處理；剛建立的短網址轉址讀到幾秒前的舊資料沒有影響，checkout 的付款狀態讀到舊資料會讓使用者重複付款。每個系統都有一件必須一次只處理一筆的事，它的互斥範圍（哪些請求彼此必須輪流處理，定義在 &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/load-path-analysis/" data-link-title="9.0 請求路徑上的負載分析：一次一筆的處理、在它之前能回應的請求、多出來的請求怎麼處置、保護把負載轉到哪裡、重送與去重，以及保護元件與依賴故障時的處置" data-link-desc="沿著一個會改變狀態的請求從用戶端到資料庫的路徑分析負載時共用的幾個概念：必須一次只處理一筆的那件事與它的互斥範圍、需要經過它的請求裡結果事先已確定的比例、多出來的請求要排隊、拒絕還是請使用者重新確認、每一段保護把負載轉給了哪個元件（保護元件自己的狀態、連線數、輪詢、下游接收端、外部配額）、佇列與重按帶來的重送怎麼去重、保護元件故障或輸出錯的值時下游讀到什麼，以及依賴變慢、部分失效與恢復時的第二波湧入">9.0 請求路徑上的負載分析&lt;/a> 的〈一次一筆的處理與它的互斥範圍〉）各自不同：售票是同一張票（座位）的認領與訂單狀態，撮合是同一個交易對的整本委託簿，checkout 是同一筆訂單的付款狀態，短網址是同一個代碼的分配。&lt;/p>
&lt;p>四個系統共用幾個部署上的前提：都在一個雲端帳號的一個區域裡（短網址是一台主機），有人值班接告警；售票的等候室自建、一次只有一個場次開賣；撮合的用戶端以程式為主，市場是連續撮合。前提不同時，各篇裡依賴它的那幾段會標出來。&lt;/p>
&lt;h2 id="短網址服務">短網址服務&lt;/h2>
&lt;p>短網址服務把一個長網址換成一段短代碼，使用者打開短網址時，服務回 &lt;code>301&lt;/code> 或 &lt;code>302&lt;/code> 轉址到原本的長網址。請求有三種：轉址（以代碼查出長網址，不需要登入）、建立與管理短網址（登入之後建立、修改、停用自己的短網址），以及管理者的後台操作（停用被檢舉的短網址、查看帳號）。轉址的量遠大於其他兩種，示意的比例是轉址與建立一百比一。&lt;/p>
&lt;p>&lt;strong>不能讓步的需求&lt;/strong>：轉址的目標必須是短網址目前登記的那一個長網址；寫錯的轉址會把使用者送到錯的網站，而主機、容器與請求的監控數字都正常。代碼不能重複分配給兩個長網址。帳號只能修改自己的短網址。管理者停用一個被檢舉的短網址之後，要在幾分鐘內（示意）停止轉址，所以轉址回應的快取期限有上限。這個範例允許建立者修改目的地，修改之後要重新做惡意網址檢查並保留修改紀錄；目的地是否允許修改、撤回要多快生效，是短網址服務要先定案的需求決定，見 &lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/url-shortener-requirements/" data-link-title="0.23 短網址服務的需求定義：自建或購買、保存期限、目的地修改與濫用處理的規格" data-link-desc="短網址服務交給工程之前要定案的需求決定：自建或購買、點擊資料的地位、連結的保存承諾、目的地能否修改、建立者的範圍、短碼的可猜測性、網域策略與可用性分級；多數答案由商業模型推出，目的地修改、短碼可猜測性與可用性分級由使用情境決定">0.23 短網址服務的需求定義&lt;/a> 的〈目的地修改的功能需求與風險〉與 &lt;a href="https://tarrragon.github.io/blog/backend/00-service-selection/url-shortener-implementation/" data-link-title="0.24 短網址服務的實作：短碼生成、對照表儲存、轉址快取與點擊紀錄" data-link-desc="以需求已定案為前提的實作取捨：建立與轉址兩條路徑的流量形狀、各種短碼生成方式的可猜測性與協調成本、轉址狀態碼與各層快取怎麼影響停用或修改連結多久生效、點擊事件的非同步紀錄、濫用防護、服務結束時的靜態轉址與網域去向，以及換掉任一項需求決定時要重選的實作位置">0.24 短網址服務的實作&lt;/a> 的〈各層快取與撤回所需的時間〉。&lt;/p>
&lt;p>&lt;strong>可以讓步的需求&lt;/strong>：剛建立的短網址在幾秒內轉址失敗，影響的是一個剛建好、還沒散出去的連結；轉址的延遲多個幾十毫秒，使用者感覺不到。點擊次數的統計可以延遲、可以少算幾筆。&lt;/p>
&lt;p>&lt;strong>規模假設（示意）&lt;/strong>：每秒數百次轉址，尖峰是平常的數倍（一則被大量轉貼的連結）；短網址總數在百萬筆的量級。&lt;/p>
&lt;p>&lt;strong>起點架構&lt;/strong>：一台主機上以 Docker Compose 跑入口 Nginx、後端程式、PostgreSQL 與 Valkey，各在一個容器裡；Valkey 快取代碼到長網址的對應。監控的分層以它為例，見 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/monitoring-layers/" data-link-title="4.0 服務端監控的分層：容器與程序的自檢、服務網路內的探測、元件指標、應用層訊號、告警規則與分送、告警鏈的監看與使用者路徑的合成監控" data-link-desc="以每一層監控檢查的對象、看不到的範圍與接手的那一層，把服務端監控串成一條監控鏈，以及拿它盤點一個服務時怎麼判讀缺口的性質（檢查範圍、位置、讀取與訊號失真），涵蓋自架主機、Kubernetes 與雲端託管的情形；使用者路徑那一層另寫合成監控與真實使用者監測的差別，以及盤點時各種缺口的補法">4.0 服務端監控的分層&lt;/a>，實際的盤點見 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/monitoring-inventory-single-host/" data-link-title="4.0.2 以監控鏈盤點單機短網址服務：各層現有的監控、缺口的性質與補法，以及監控鏈事前偵測不到的異常" data-link-desc="把一台主機上以 Docker Compose 跑的短網址服務現有的每一種監控放進監控鏈的各層，逐層判讀還開著的缺口屬於檢查範圍盲區、位置盲區、讀取缺口還是訊號失真，各自的補法，以及補上失效域以外的觀察者之前，主機整台停掉時沒有任何一層會通知的情形；另逐類查業務結果、資料正確性、安全、成本、到期事件與備份">4.0.2 以監控鏈盤點單機短網址服務&lt;/a>。&lt;/p>
&lt;h2 id="售票搶購">售票搶購&lt;/h2>
&lt;p>售票系統在指定的開賣時間讓使用者選場次、選座位或票種、下單並在期限內付款。請求有四種：讀取場次與座位圖、排隊或取得購票資格、扣庫存下單、付款與付款結果的回呼。開賣前幾秒到開賣後幾分鐘的請求量是平常的數百倍，票賣完之後請求量迅速回落。&lt;/p>
&lt;p>&lt;strong>不能讓步的需求&lt;/strong>：不超賣，賣出的票數不能超過場次的票數，同一個座位不能賣給兩個人。付款成功的訂單一定要有票。付款期限（示意：十分鐘）到期而未付款的票要放回可售，放回與付款不能同時成立。&lt;/p>
&lt;p>&lt;strong>可以讓步的需求&lt;/strong>：使用者可以接受排隊幾分鐘，等候期間看到自己的位置即可；座位圖上的剩餘數量可以落後幾秒，因為最後的判斷在扣庫存那一步；票在開賣後的前幾秒沒有全部賣出（少賣），只要之後回收得回來，是可以接受的短暫狀態。誰先買到票的順序依業務決定（先到先得或抽籤），而不論哪一種，規則要事先公布並照做。&lt;/p>
&lt;p>&lt;strong>成功請求的總數有上限&lt;/strong>：成功的購票總數不會超過票數。十萬個帳號搶五千張票、每個帳號限購一張時（示意），票賣完那一刻，其餘九萬五千人的購票請求結果已經確定是售完（直到付款期限到期、有票被放回為止）。&lt;/p>
&lt;p>&lt;strong>規模假設（示意）&lt;/strong>：一個場次五千張票，開賣時十萬人同時在線；開賣後第一分鐘各種請求加起來每秒數萬個，其中扣庫存的請求受排隊放行的速率限制。&lt;/p>
&lt;p>&lt;strong>起點架構&lt;/strong>：CDN 與入口、無狀態的 API 服務（可水平擴充）、排隊服務、關聯式資料庫存放每張票一列與訂單、第三方金流處理付款、排程工作處理付款期限。站內的對照案例有 &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/tixcraft-ticketing-flash-sale-spike/" data-link-title="9.C15 拓元 Tixcraft：售票搶購的瞬間爆量架構" data-link-desc="拓元把售票系統搬到 AWS 後，30 分鐘內把資源擴到 130 倍（一場熱門活動擴到 800 台 EC2），處理 10 萬以上同時在線與每秒 2,500 筆以上訂單；資料庫改用 DynamoDB">9.C15 拓元 Tixcraft：售票搶購的瞬間爆量架構&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/seatgeek-virtual-waiting-room/" data-link-title="9.C16 SeatGeek：DynamoDB &amp;#43; Lambda 打造的虛擬等候室" data-link-desc="SeatGeek 用 DynamoDB 4 張表 &amp;#43; Lambda Bouncer 實作 flash-sale 的准入控制（等候室）、取代第三方 waiting room 服務">9.C16 SeatGeek：DynamoDB + Lambda 打造的虛擬等候室&lt;/a>；負載路徑的分析，包括什麼條件下才在扣庫存前面加一道 Redis 閘門，見 &lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/ticketing-load-path/" data-link-title="9.0.1 售票搶購的負載路徑：熱點列與連線池、CDN 合併回源、排隊放行的速率、資料庫認領與加入 Redis 閘門的條件、付款期限的條件更新" data-link-desc="以售票搶購為例，沿著一張票從座位圖到付款記帳的路徑，寫出全部請求更新同一個庫存計數時熱點列與連線池的耗盡順序，邊緣快取、排隊放行、資料庫逐張認領與加入 Redis 扣庫存各自把負載轉給了哪一段、自己出錯時由哪一段補救，以及成功請求的總數有上限、延遲不改變請求的價值、先到先得這幾個前提讓哪些做法可以簡化、哪些需求無法簡化">9.0.1 售票搶購的負載路徑&lt;/a>。&lt;/p>
&lt;h2 id="電商-checkout">電商 checkout&lt;/h2>
&lt;p>電商的 checkout 讓使用者把購物車裡的商品下單、付款，之後由下游寄送確認信、更新庫存與報表。請求有：讀取商品與價格、建立訂單、送出付款、接收金流業者的付款結果回呼；訂單建立之後的通知、出貨與報表由訊息佇列的消費端非同步處理。這是 &lt;a href="https://tarrragon.github.io/blog/backend/reading-paths/" data-link-title="Backend 教材的閱讀路線：教材的分層、依讀者目的排的模組順序與貫穿各模組的 checkout 範例流程" data-link-desc="Backend 教材各模組之間的閱讀順序：分類語言、服務路徑與具體工具的分層，依讀者目的（把服務上線、設計 API、學 production 操作、看懂資安控制、比較工具、讓服務撐住成長）排的路線，以及各模組在一條 checkout 流程裡處理的問題">Backend 教材的閱讀路線&lt;/a> 裡貫穿各模組的 checkout 流程。&lt;/p></description><content:encoded><![CDATA[<p>這篇定義四個範例系統：短網址、售票搶購、電商 checkout 與交易撮合，供幾篇沿著具體系統的請求路徑逐段分析的文章共用，目前有 <a href="/blog/backend/09-performance-capacity/load-path-analysis/" data-link-title="9.0 請求路徑上的負載分析：一次一筆的處理、在它之前能回應的請求、多出來的請求怎麼處置、保護把負載轉到哪裡、重送與去重，以及保護元件與依賴故障時的處置" data-link-desc="沿著一個會改變狀態的請求從用戶端到資料庫的路徑分析負載時共用的幾個概念：必須一次只處理一筆的那件事與它的互斥範圍、需要經過它的請求裡結果事先已確定的比例、多出來的請求要排隊、拒絕還是請使用者重新確認、每一段保護把負載轉給了哪個元件（保護元件自己的狀態、連線數、輪詢、下游接收端、外部配額）、佇列與重按帶來的重送怎麼去重、保護元件故障或輸出錯的值時下游讀到什麼，以及依賴變慢、部分失效與恢復時的第二波湧入">9.0 請求路徑上的負載分析</a> 的舉例、<a href="/blog/backend/09-performance-capacity/ticketing-load-path/" data-link-title="9.0.1 售票搶購的負載路徑：熱點列與連線池、CDN 合併回源、排隊放行的速率、資料庫認領與加入 Redis 閘門的條件、付款期限的條件更新" data-link-desc="以售票搶購為例，沿著一張票從座位圖到付款記帳的路徑，寫出全部請求更新同一個庫存計數時熱點列與連線池的耗盡順序，邊緣快取、排隊放行、資料庫逐張認領與加入 Redis 扣庫存各自把負載轉給了哪一段、自己出錯時由哪一段補救，以及成功請求的總數有上限、延遲不改變請求的價值、先到先得這幾個前提讓哪些做法可以簡化、哪些需求無法簡化">9.0.1 售票搶購的負載路徑</a>、<a href="/blog/backend/09-performance-capacity/matching-engine-load-path/" data-link-title="9.0.2 交易撮合的負載路徑：閘道的帳戶限流與明確拒絕、依序日誌的複寫延遲、單一寫入者的撮合引擎、行情的合併送出與重放驗證容量" data-link-desc="以交易撮合為例，沿著一筆委託從入口閘道到成交回報的路徑，寫出撮合之前結果無法確定、處理順序決定成交、延遲改變請求的價值這些性質讓哪些負載保護不能用，閘道、依序日誌、單一寫入者的撮合引擎與行情發布各自先耗盡的資源與出錯時的補救，過載時明確拒絕超量委託與以重放驗證容量的做法，並與售票搶購對照">9.0.2 交易撮合的負載路徑</a>、<a href="/blog/backend/04-observability/monitoring-inventory-single-host/" data-link-title="4.0.2 以監控鏈盤點單機短網址服務：各層現有的監控、缺口的性質與補法，以及監控鏈事前偵測不到的異常" data-link-desc="把一台主機上以 Docker Compose 跑的短網址服務現有的每一種監控放進監控鏈的各層，逐層判讀還開著的缺口屬於檢查範圍盲區、位置盲區、讀取缺口還是訊號失真，各自的補法，以及補上失效域以外的觀察者之前，主機整台停掉時沒有任何一層會通知的情形；另逐類查業務結果、資料正確性、安全、成本、到期事件與備份">4.0.2 以監控鏈盤點單機短網址服務</a> 、<a href="/blog/backend/04-observability/monitoring-inventory-k8s-checkout/" data-link-title="4.0.3 以監控鏈盤點 Kubernetes 上的電商 checkout：叢集內的監控、託管資料庫與第三方金流各自的失效域，以及佇列消費端與外部依賴的缺口" data-link-desc="把一個跑在託管 Kubernetes 上、有訊息佇列消費端、託管資料庫與第三方金流的電商 checkout 現有的監控放進監控鏈的各層，逐層判讀缺口與補法；另寫付款服務的 readiness 納入金流業者時對付款回呼的影響、失效域從主機換成叢集與雲端帳號之後觀察者要放在哪裡，以及業務結果、對帳、到期事件、消費端、備份、安全、第三方依賴與成本的逐類盤點">4.0.3 以監控鏈盤點 Kubernetes 上的電商 checkout</a> 與 <a href="/blog/backend/04-observability/monitoring-inventory-matching-engine/" data-link-title="4.0.4 以監控鏈盤點交易撮合系統：觀察者讀數字的位置、沒有新委託時分辨市場安靜與路徑停住、分段量測延遲，以及委託序號、帳戶部位的對帳與其他監控鏈事前偵測不到的異常" data-link-desc="把一個交易撮合系統的監控放進監控鏈的各層判讀缺口：撮合引擎單一執行緒時觀察者為什麼要從輸出日誌讀數字、沒有新委託時怎麼分辨市場安靜還是處理路徑停住、委託延遲怎麼依段量測與時鐘同步的前提、依序日誌多數節點與同一可用區的失效域，以及輸出序號的斷號、帳戶部位與行情資料的一致性這類監控鏈事前偵測不到的異常">4.0.4 以監控鏈盤點交易撮合系統</a>。那幾篇引用的請求量、票數這類規模數字是本篇的假設，用來讓例子有具體的量，不是任何真實服務的數字；<a href="/blog/backend/04-observability/host-traffic-resource-monitoring/" data-link-title="4.26 單機容器服務的流量與資源監控：各層的訊號、Prometheus 與 exporter、監控所在層的盲區" data-link-desc="健康檢查之外的流量與資源監控：入口、應用程式、容器、主機與資料庫等服務內部各層的訊號從哪裡收、用哪一個 exporter，Prometheus 加 exporter、單一 agent、開箱即用工具與臨時查看指令的取捨，單次取樣與時間窗的差別，監控裝在虛擬機裡時看不到主機的 CPU 負載、看到的磁碟要分清是哪一顆，以及每接上一個來源的驗收與偵測容器停止的寫法；cAdvisor 與 node_exporter 在 OrbStack 上實測過">4.26 單機容器服務的流量與資源監控</a> 到 <a href="/blog/backend/04-observability/single-host-monitoring-outside-view/" data-link-title="4.28 單台主機的監控盲區與失效域以外的觀察者：主機停機、使用者路徑中斷與告警鏈中斷，心跳監看與外部探測各自回答的問題與位置，以及與內部探測並排的判讀" data-link-desc="服務與監控放在同一台主機時監控結構上看不到的故障：主機整台停掉、使用者到主機的路徑中斷、告警鏈停掉與通知送不出去；從失效域以外送出訊號的心跳監看與外部探測各自回答的問題與成立條件，觀察者由第三方服務、另一個失效域的主機（含兩台主機互相監看）或另一個業者的排程平台提供時的差別，以及心跳監看、外部探測與主機內部探測並排時的判讀">4.28 單台主機的監控盲區與失效域以外的觀察者</a> 也用短網址服務當例子，那幾篇的量測數字來自實際的開發環境，不在本篇的假設之內。</p>
<p>同一個元件在不同需求下的設計不同。售票系統可以讓大多數請求在進資料庫之前就收到「售完」，交易撮合每一筆被接受的委託都要處理；剛建立的短網址轉址讀到幾秒前的舊資料沒有影響，checkout 的付款狀態讀到舊資料會讓使用者重複付款。每個系統都有一件必須一次只處理一筆的事，它的互斥範圍（哪些請求彼此必須輪流處理，定義在 <a href="/blog/backend/09-performance-capacity/load-path-analysis/" data-link-title="9.0 請求路徑上的負載分析：一次一筆的處理、在它之前能回應的請求、多出來的請求怎麼處置、保護把負載轉到哪裡、重送與去重，以及保護元件與依賴故障時的處置" data-link-desc="沿著一個會改變狀態的請求從用戶端到資料庫的路徑分析負載時共用的幾個概念：必須一次只處理一筆的那件事與它的互斥範圍、需要經過它的請求裡結果事先已確定的比例、多出來的請求要排隊、拒絕還是請使用者重新確認、每一段保護把負載轉給了哪個元件（保護元件自己的狀態、連線數、輪詢、下游接收端、外部配額）、佇列與重按帶來的重送怎麼去重、保護元件故障或輸出錯的值時下游讀到什麼，以及依賴變慢、部分失效與恢復時的第二波湧入">9.0 請求路徑上的負載分析</a> 的〈一次一筆的處理與它的互斥範圍〉）各自不同：售票是同一張票（座位）的認領與訂單狀態，撮合是同一個交易對的整本委託簿，checkout 是同一筆訂單的付款狀態，短網址是同一個代碼的分配。</p>
<p>四個系統共用幾個部署上的前提：都在一個雲端帳號的一個區域裡（短網址是一台主機），有人值班接告警；售票的等候室自建、一次只有一個場次開賣；撮合的用戶端以程式為主，市場是連續撮合。前提不同時，各篇裡依賴它的那幾段會標出來。</p>
<h2 id="短網址服務">短網址服務</h2>
<p>短網址服務把一個長網址換成一段短代碼，使用者打開短網址時，服務回 <code>301</code> 或 <code>302</code> 轉址到原本的長網址。請求有三種：轉址（以代碼查出長網址，不需要登入）、建立與管理短網址（登入之後建立、修改、停用自己的短網址），以及管理者的後台操作（停用被檢舉的短網址、查看帳號）。轉址的量遠大於其他兩種，示意的比例是轉址與建立一百比一。</p>
<p><strong>不能讓步的需求</strong>：轉址的目標必須是短網址目前登記的那一個長網址；寫錯的轉址會把使用者送到錯的網站，而主機、容器與請求的監控數字都正常。代碼不能重複分配給兩個長網址。帳號只能修改自己的短網址。管理者停用一個被檢舉的短網址之後，要在幾分鐘內（示意）停止轉址，所以轉址回應的快取期限有上限。這個範例允許建立者修改目的地，修改之後要重新做惡意網址檢查並保留修改紀錄；目的地是否允許修改、撤回要多快生效，是短網址服務要先定案的需求決定，見 <a href="/blog/backend/00-service-selection/url-shortener-requirements/" data-link-title="0.23 短網址服務的需求定義：自建或購買、保存期限、目的地修改與濫用處理的規格" data-link-desc="短網址服務交給工程之前要定案的需求決定：自建或購買、點擊資料的地位、連結的保存承諾、目的地能否修改、建立者的範圍、短碼的可猜測性、網域策略與可用性分級；多數答案由商業模型推出，目的地修改、短碼可猜測性與可用性分級由使用情境決定">0.23 短網址服務的需求定義</a> 的〈目的地修改的功能需求與風險〉與 <a href="/blog/backend/00-service-selection/url-shortener-implementation/" data-link-title="0.24 短網址服務的實作：短碼生成、對照表儲存、轉址快取與點擊紀錄" data-link-desc="以需求已定案為前提的實作取捨：建立與轉址兩條路徑的流量形狀、各種短碼生成方式的可猜測性與協調成本、轉址狀態碼與各層快取怎麼影響停用或修改連結多久生效、點擊事件的非同步紀錄、濫用防護、服務結束時的靜態轉址與網域去向，以及換掉任一項需求決定時要重選的實作位置">0.24 短網址服務的實作</a> 的〈各層快取與撤回所需的時間〉。</p>
<p><strong>可以讓步的需求</strong>：剛建立的短網址在幾秒內轉址失敗，影響的是一個剛建好、還沒散出去的連結；轉址的延遲多個幾十毫秒，使用者感覺不到。點擊次數的統計可以延遲、可以少算幾筆。</p>
<p><strong>規模假設（示意）</strong>：每秒數百次轉址，尖峰是平常的數倍（一則被大量轉貼的連結）；短網址總數在百萬筆的量級。</p>
<p><strong>起點架構</strong>：一台主機上以 Docker Compose 跑入口 Nginx、後端程式、PostgreSQL 與 Valkey，各在一個容器裡；Valkey 快取代碼到長網址的對應。監控的分層以它為例，見 <a href="/blog/backend/04-observability/monitoring-layers/" data-link-title="4.0 服務端監控的分層：容器與程序的自檢、服務網路內的探測、元件指標、應用層訊號、告警規則與分送、告警鏈的監看與使用者路徑的合成監控" data-link-desc="以每一層監控檢查的對象、看不到的範圍與接手的那一層，把服務端監控串成一條監控鏈，以及拿它盤點一個服務時怎麼判讀缺口的性質（檢查範圍、位置、讀取與訊號失真），涵蓋自架主機、Kubernetes 與雲端託管的情形；使用者路徑那一層另寫合成監控與真實使用者監測的差別，以及盤點時各種缺口的補法">4.0 服務端監控的分層</a>，實際的盤點見 <a href="/blog/backend/04-observability/monitoring-inventory-single-host/" data-link-title="4.0.2 以監控鏈盤點單機短網址服務：各層現有的監控、缺口的性質與補法，以及監控鏈事前偵測不到的異常" data-link-desc="把一台主機上以 Docker Compose 跑的短網址服務現有的每一種監控放進監控鏈的各層，逐層判讀還開著的缺口屬於檢查範圍盲區、位置盲區、讀取缺口還是訊號失真，各自的補法，以及補上失效域以外的觀察者之前，主機整台停掉時沒有任何一層會通知的情形；另逐類查業務結果、資料正確性、安全、成本、到期事件與備份">4.0.2 以監控鏈盤點單機短網址服務</a>。</p>
<h2 id="售票搶購">售票搶購</h2>
<p>售票系統在指定的開賣時間讓使用者選場次、選座位或票種、下單並在期限內付款。請求有四種：讀取場次與座位圖、排隊或取得購票資格、扣庫存下單、付款與付款結果的回呼。開賣前幾秒到開賣後幾分鐘的請求量是平常的數百倍，票賣完之後請求量迅速回落。</p>
<p><strong>不能讓步的需求</strong>：不超賣，賣出的票數不能超過場次的票數，同一個座位不能賣給兩個人。付款成功的訂單一定要有票。付款期限（示意：十分鐘）到期而未付款的票要放回可售，放回與付款不能同時成立。</p>
<p><strong>可以讓步的需求</strong>：使用者可以接受排隊幾分鐘，等候期間看到自己的位置即可；座位圖上的剩餘數量可以落後幾秒，因為最後的判斷在扣庫存那一步；票在開賣後的前幾秒沒有全部賣出（少賣），只要之後回收得回來，是可以接受的短暫狀態。誰先買到票的順序依業務決定（先到先得或抽籤），而不論哪一種，規則要事先公布並照做。</p>
<p><strong>成功請求的總數有上限</strong>：成功的購票總數不會超過票數。十萬個帳號搶五千張票、每個帳號限購一張時（示意），票賣完那一刻，其餘九萬五千人的購票請求結果已經確定是售完（直到付款期限到期、有票被放回為止）。</p>
<p><strong>規模假設（示意）</strong>：一個場次五千張票，開賣時十萬人同時在線；開賣後第一分鐘各種請求加起來每秒數萬個，其中扣庫存的請求受排隊放行的速率限制。</p>
<p><strong>起點架構</strong>：CDN 與入口、無狀態的 API 服務（可水平擴充）、排隊服務、關聯式資料庫存放每張票一列與訂單、第三方金流處理付款、排程工作處理付款期限。站內的對照案例有 <a href="/blog/backend/09-performance-capacity/cases/tixcraft-ticketing-flash-sale-spike/" data-link-title="9.C15 拓元 Tixcraft：售票搶購的瞬間爆量架構" data-link-desc="拓元把售票系統搬到 AWS 後，30 分鐘內把資源擴到 130 倍（一場熱門活動擴到 800 台 EC2），處理 10 萬以上同時在線與每秒 2,500 筆以上訂單；資料庫改用 DynamoDB">9.C15 拓元 Tixcraft：售票搶購的瞬間爆量架構</a> 與 <a href="/blog/backend/09-performance-capacity/cases/seatgeek-virtual-waiting-room/" data-link-title="9.C16 SeatGeek：DynamoDB &#43; Lambda 打造的虛擬等候室" data-link-desc="SeatGeek 用 DynamoDB 4 張表 &#43; Lambda Bouncer 實作 flash-sale 的准入控制（等候室）、取代第三方 waiting room 服務">9.C16 SeatGeek：DynamoDB + Lambda 打造的虛擬等候室</a>；負載路徑的分析，包括什麼條件下才在扣庫存前面加一道 Redis 閘門，見 <a href="/blog/backend/09-performance-capacity/ticketing-load-path/" data-link-title="9.0.1 售票搶購的負載路徑：熱點列與連線池、CDN 合併回源、排隊放行的速率、資料庫認領與加入 Redis 閘門的條件、付款期限的條件更新" data-link-desc="以售票搶購為例，沿著一張票從座位圖到付款記帳的路徑，寫出全部請求更新同一個庫存計數時熱點列與連線池的耗盡順序，邊緣快取、排隊放行、資料庫逐張認領與加入 Redis 扣庫存各自把負載轉給了哪一段、自己出錯時由哪一段補救，以及成功請求的總數有上限、延遲不改變請求的價值、先到先得這幾個前提讓哪些做法可以簡化、哪些需求無法簡化">9.0.1 售票搶購的負載路徑</a>。</p>
<h2 id="電商-checkout">電商 checkout</h2>
<p>電商的 checkout 讓使用者把購物車裡的商品下單、付款，之後由下游寄送確認信、更新庫存與報表。請求有：讀取商品與價格、建立訂單、送出付款、接收金流業者的付款結果回呼；訂單建立之後的通知、出貨與報表由訊息佇列的消費端非同步處理。這是 <a href="/blog/backend/reading-paths/" data-link-title="Backend 教材的閱讀路線：教材的分層、依讀者目的排的模組順序與貫穿各模組的 checkout 範例流程" data-link-desc="Backend 教材各模組之間的閱讀順序：分類語言、服務路徑與具體工具的分層，依讀者目的（把服務上線、設計 API、學 production 操作、看懂資安控制、比較工具、讓服務撐住成長）排的路線，以及各模組在一條 checkout 流程裡處理的問題">Backend 教材的閱讀路線</a> 裡貫穿各模組的 checkout 流程。</p>
<p><strong>不能讓步的需求</strong>：同一筆訂單只能扣款一次，使用者重按付款或用戶端重試都不能多扣；付款成功的訂單不能遺失；訂單金額必須等於使用者確認時看到的金額。</p>
<p><strong>可以讓步的需求</strong>：確認信晚幾分鐘寄出、報表晚一小時更新，使用者與營運都能接受；商品頁的價格快取可以落後，只要下單時以資料庫的價格重新計算並在金額不同時請使用者確認。</p>
<p><strong>成功請求的總數沒有固定上限</strong>：庫存充足時，每一個下單請求都可能成交，擋掉一個就是少一筆營收。</p>
<p><strong>規模假設（示意）</strong>：平日每秒數十筆訂單，促銷活動時是平常的十倍；商品頁的讀取是下單的數百倍。</p>
<p><strong>起點架構</strong>：Kubernetes 上的商品、訂單與付款服務，PostgreSQL 存放訂單，Redis 快取商品與價格，訊息佇列連接訂單服務與通知、出貨、報表的消費端，付款服務呼叫第三方金流並接收回呼。監控的盤點見 <a href="/blog/backend/04-observability/monitoring-inventory-k8s-checkout/" data-link-title="4.0.3 以監控鏈盤點 Kubernetes 上的電商 checkout：叢集內的監控、託管資料庫與第三方金流各自的失效域，以及佇列消費端與外部依賴的缺口" data-link-desc="把一個跑在託管 Kubernetes 上、有訊息佇列消費端、託管資料庫與第三方金流的電商 checkout 現有的監控放進監控鏈的各層，逐層判讀缺口與補法；另寫付款服務的 readiness 納入金流業者時對付款回呼的影響、失效域從主機換成叢集與雲端帳號之後觀察者要放在哪裡，以及業務結果、對帳、到期事件、消費端、備份、安全、第三方依賴與成本的逐類盤點">4.0.3 以監控鏈盤點 Kubernetes 上的電商 checkout</a>。</p>
<h2 id="交易撮合">交易撮合</h2>
<p>交易撮合系統接收買方與賣方的委託，在委託簿裡配對成交。<strong>交易對</strong>（trading pair）是一組可以互相交易的資產，例如 BTC 對 USD；<strong>委託簿</strong>（order book）是一個交易對上還沒成交的買賣委託，依價格與時間排成的清單；撮合依價格與時間的優先順序（price-time priority：價格較好的先成交，同價格時先到的先成交；CME 的文件稱這條規則為 FIFO）把新進的委託與簿上的對手配對。成交之後，系統送出成交回報給雙方，更新雙方的<strong>部位</strong>（position，帳戶持有的各項資產數量），並把行情（最新成交價、各價位的委託量）送給訂閱者。請求有：送出委託、取消委託、查詢帳戶與委託狀態、訂閱行情。</p>
<p><strong>不能讓步的需求</strong>：每一筆被接受的委託都要處理，處理不了的要在接受之前明確拒絕；處理的順序必須與到達撮合引擎的順序一致，因為順序決定誰成交；同一筆委託不能執行兩次；已回應「已接受」的委託不能遺失。</p>
<p><strong>可以讓步的需求</strong>：行情送給處理不及的訂閱者時，可以合併中間的報價更新只送最新的狀態；帳戶的歷史查詢可以讀稍舊的副本。</p>
<p><strong>延遲改變請求的價值</strong>：在連續撮合的市場裡（委託一到就與簿上的對手配對），一筆委託在佇列裡多等一秒，送進委託簿時市場已經不是使用者下單時的市場。市價單會以偏離預期的價格成交；限價單不會比限價差，代價是掛著的價格已經過時、在市場變動後被以不利的時機成交。</p>
<p><strong>規模假設（示意）</strong>：一個熱門交易對每秒數萬筆委託與取消，市場劇烈波動時是平常的十倍以上；撮合的延遲目標在毫秒以下。</p>
<p><strong>起點架構</strong>：入口閘道（驗證身分、檢查餘額與帳戶的速率限制）、依交易對分開的撮合引擎、委託先寫入依序編號的日誌再撮合、行情發布服務、帳戶與歷史資料存放在資料庫。站內的對照案例有 <a href="/blog/backend/09-performance-capacity/cases/coinbase-ultra-low-latency-exchange-2023/" data-link-title="9.C3 Coinbase International Exchange：超低延遲交易的逆向容量設計" data-link-desc="為什麼 Coinbase 國際交易所選 Cluster Placement Group &#43; z1d 而不是自動擴容 — 延遲敏感型負載的容量取捨">9.C3 Coinbase International Exchange：超低延遲交易的逆向容量設計</a>，負載路徑的分析見 <a href="/blog/backend/09-performance-capacity/matching-engine-load-path/" data-link-title="9.0.2 交易撮合的負載路徑：閘道的帳戶限流與明確拒絕、依序日誌的複寫延遲、單一寫入者的撮合引擎、行情的合併送出與重放驗證容量" data-link-desc="以交易撮合為例，沿著一筆委託從入口閘道到成交回報的路徑，寫出撮合之前結果無法確定、處理順序決定成交、延遲改變請求的價值這些性質讓哪些負載保護不能用，閘道、依序日誌、單一寫入者的撮合引擎與行情發布各自先耗盡的資源與出錯時的補救，過載時明確拒絕超量委託與以重放驗證容量的做法，並與售票搶購對照">9.0.2 交易撮合的負載路徑</a>，監控的盤點見 <a href="/blog/backend/04-observability/monitoring-inventory-matching-engine/" data-link-title="4.0.4 以監控鏈盤點交易撮合系統：觀察者讀數字的位置、沒有新委託時分辨市場安靜與路徑停住、分段量測延遲，以及委託序號、帳戶部位的對帳與其他監控鏈事前偵測不到的異常" data-link-desc="把一個交易撮合系統的監控放進監控鏈的各層判讀缺口：撮合引擎單一執行緒時觀察者為什麼要從輸出日誌讀數字、沒有新委託時怎麼分辨市場安靜還是處理路徑停住、委託延遲怎麼依段量測與時鐘同步的前提、依序日誌多數節點與同一可用區的失效域，以及輸出序號的斷號、帳戶部位與行情資料的一致性這類監控鏈事前偵測不到的異常">4.0.4 以監控鏈盤點交易撮合系統</a>。</p>
<h2 id="四個系統在請求性質上的差別">四個系統在請求性質上的差別</h2>
<p>四個系統在下面三個性質上的位置。第一欄只對要經過那件一次一筆處理的請求有意義（購票、下單、委託、建立短網址），轉址、查商品這類讀取不在其中：</p>
<table>
  <thead>
      <tr>
          <th>系統</th>
          <th>成功請求的總數</th>
          <th>延遲是否改變請求的價值</th>
          <th>同一個請求被處理兩次的後果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>短網址</td>
          <td>無上限，每一次建立都要成功</td>
          <td>幾十毫秒內不改變</td>
          <td>轉址重複無害；建立重複會多一個代碼</td>
      </tr>
      <tr>
          <td>售票搶購</td>
          <td>有上限：票數；售完之後的請求結果已確定</td>
          <td>不劃位、固定票價時不改變：排隊幾分鐘之後買到的仍是同一種票、同一個價格；排隊決定的是誰買到；對號入座時改變的是剩下哪些座位，動態定價時連價格也變</td>
          <td>同一人買到兩張或扣兩次款</td>
      </tr>
      <tr>
          <td>電商 checkout</td>
          <td>庫存充足時無上限，每一筆都可能成交</td>
          <td>幾秒內不改變，太久使用者放棄</td>
          <td>重複扣款</td>
      </tr>
      <tr>
          <td>交易撮合</td>
          <td>無上限，每一筆被接受的委託都要處理</td>
          <td>改變：毫秒等級就影響成交價格或成交時機</td>
          <td>重複成交，帳戶部位錯誤</td>
      </tr>
  </tbody>
</table>
<p>三個性質怎麼用來判斷負載保護的方向（第一欄只是可提早回應的比例的一個來源，拍賣就是有上限而比例低的反例；第二欄要以「改變、所以要拒絕」判讀，還要請求處理時不再經使用者確認），在 <a href="/blog/backend/09-performance-capacity/load-path-analysis/" data-link-title="9.0 請求路徑上的負載分析：一次一筆的處理、在它之前能回應的請求、多出來的請求怎麼處置、保護把負載轉到哪裡、重送與去重，以及保護元件與依賴故障時的處置" data-link-desc="沿著一個會改變狀態的請求從用戶端到資料庫的路徑分析負載時共用的幾個概念：必須一次只處理一筆的那件事與它的互斥範圍、需要經過它的請求裡結果事先已確定的比例、多出來的請求要排隊、拒絕還是請使用者重新確認、每一段保護把負載轉給了哪個元件（保護元件自己的狀態、連線數、輪詢、下游接收端、外部配額）、佇列與重按帶來的重送怎麼去重、保護元件故障或輸出錯的值時下游讀到什麼，以及依賴變慢、部分失效與恢復時的第二波湧入">9.0 請求路徑上的負載分析</a>。延遲改變請求價值的系統不只連續撮合的交易所。廣告的即時競價要在交易平台設定的期限內回出價（期限以毫秒計，各平台不同），晚到的出價直接作廢；叫車的媒合晚了幾秒，司機與乘客的位置都變了。同一個請求被處理兩次有後果的系統，從用戶端到資料庫的每一段都要能辨認重送的請求（<a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">Idempotency</a>）。可提早回應的比例與延遲這兩個性質，加上互斥範圍能縮小到什麼程度，如何決定負載保護的方向，是 <a href="/blog/backend/09-performance-capacity/ticketing-load-path/" data-link-title="9.0.1 售票搶購的負載路徑：熱點列與連線池、CDN 合併回源、排隊放行的速率、資料庫認領與加入 Redis 閘門的條件、付款期限的條件更新" data-link-desc="以售票搶購為例，沿著一張票從座位圖到付款記帳的路徑，寫出全部請求更新同一個庫存計數時熱點列與連線池的耗盡順序，邊緣快取、排隊放行、資料庫逐張認領與加入 Redis 扣庫存各自把負載轉給了哪一段、自己出錯時由哪一段補救，以及成功請求的總數有上限、延遲不改變請求的價值、先到先得這幾個前提讓哪些做法可以簡化、哪些需求無法簡化">9.0.1 售票搶購的負載路徑</a> 與 <a href="/blog/backend/09-performance-capacity/matching-engine-load-path/" data-link-title="9.0.2 交易撮合的負載路徑：閘道的帳戶限流與明確拒絕、依序日誌的複寫延遲、單一寫入者的撮合引擎、行情的合併送出與重放驗證容量" data-link-desc="以交易撮合為例，沿著一筆委託從入口閘道到成交回報的路徑，寫出撮合之前結果無法確定、處理順序決定成交、延遲改變請求的價值這些性質讓哪些負載保護不能用，閘道、依序日誌、單一寫入者的撮合引擎與行情發布各自先耗盡的資源與出錯時的補救，過載時明確拒絕超量委託與以重放驗證容量的做法，並與售票搶購對照">9.0.2 交易撮合的負載路徑</a> 對照的主題：售票的比例高（成功請求的總數有上限，售完之後的請求結果已確定）、延遲不改變價值，撮合兩者都相反。</p>
]]></content:encoded></item></channel></rss>