📊 設計資料密集型應用
DDIA — Martin Kleppmann 資料在多台機器間如何正確流動
🎯 什麼情境該想到我
當你要評估或設計一個資料/後端系統,想有一套「該顧哪些面向」的總框架時。
⚙️ 怎麼用(三個評估軸)
- 可靠性(Reliability):即使硬體/軟體/人為出錯,仍能正確運作。→ 設計容錯,主動注入故障測試。
- 可擴展性(Scalability):先定義負載參數(QPS、讀寫比、資料量),再看負載成長時效能如何。用百分位(p95/p99)談延遲,別只看平均。
- 可維護性(Maintainability):可運維性、簡單性(控制複雜度)、可演進性。
面對任何系統,逐軸問一遍,就不會漏掉非功能面。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
- 「可擴展」不是布林值;一定要問「隨『哪個負載參數』成長」。
🔗 相關工具
資料模型與查詢語言 關聯 / 文件 / 圖模型;選模型=選「資料之間關係」的表達方式。
儲存與檢索 LSM-Tree(追加寫、寫優化)vs B-Tree(就地更新、讀優化);OLTP vs OLAP 欄式儲存。
🎯 什麼情境該想到我
當你要改資料格式/schema/API,又擔心「新舊版本同時在線」會壞掉時(滾動升級、多服務並存)。
⚙️ 怎麼用
- 顧好雙向相容:
- 向後相容:新程式能讀舊資料。
- 向前相容:舊程式能讀新資料(忽略不認得的欄位)。
兩者都要,才能新舊版本共存、滾動部署。
- 選編碼格式:JSON/XML(人可讀、無 schema 演進保證)vs Protobuf/Avro/Thrift(緊湊、有明確的 schema 演進規則)。
- 加欄位用「可選 + 預設值」,別移除或改動既有欄位的標籤/型別。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
- 「改一個必填欄位的意義」是隱形殺手;演進規則要跟著格式走。
🔗 相關工具
- 對照 工具-介面設計原則(介面同樣要能演進)
🧱 第一部:資料系統基礎
🎯 什麼情境該想到我
當你想讓資料庫高可用、或用多副本分攤讀取,卻遇到「讀到舊資料」的怪象時。
⚙️ 怎麼用(先選架構,再處理延遲)
- 選複製架構:
- 單主(Leader-based):寫主、讀從,最常見;簡單但主是寫入瓶頸/單點。
- 多主:跨資料中心可寫,但要處理寫衝突。
- 無主(Dynamo 式):靠 quorum 讀寫。
- 同步 vs 非同步:同步保證從庫有最新值但慢/易卡;非同步快但有延遲。
- 處理複製延遲的讀取異常:
- 讀自己的寫 → 讀己所寫一致性(read-your-writes)
- 時光倒流 → 單調讀(monotonic reads)
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
- 非同步複製下「主故障切換」可能丟資料;切換策略要想清楚。
- worker 查從庫可能查不到剛寫的資料 → 見 工具-MQ消費端防禦三原則。
🔗 相關工具
- 工具-資料分區 —— 姊妹題,複製解決可用性與讀取分攤,分區解決容量與寫入擴展
- 工具-線性一致性與共識 —— 複製帶來的頭痛問題,讀到舊資料要不要修、修到什麼程度
🎯 什麼情境該想到我
當單一機器裝不下資料/扛不住流量,需要把資料切分到多台機器(水平擴展)時。
⚙️ 怎麼用
- 選分區鍵與策略:
- 按範圍分區:範圍查詢方便,但易產生熱點(如全部照時間寫入同一分區)。
- 按雜湊分區:分佈均勻、避熱點,但失去範圍查詢能力。
- 處理熱點:熱鍵可加隨機前綴打散。
- 次級索引:本地索引(分區內,查詢要 scatter/gather)vs 全域索引(寫入要跨分區)。
- 再平衡(rebalancing):用固定數量分區等策略,避免資料大搬風。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
- 分區鍵選錯(造成熱點或常需跨分區查詢)事後極難改,設計時最關鍵。
🔗 相關工具
- 工具-資料複製(分區常與複製並用)、工具-可靠可擴展可維護
🎯 什麼情境該想到我
當多個請求同時讀寫同一批資料、出現「錯亂/覆蓋/看到中間狀態」的併發問題時。
⚙️ 怎麼用(依你要防的異常挑隔離等級)
- 讀已提交(Read Committed):防髒讀、髒寫。最低堪用等級。
- 快照隔離 / 可重複讀(Snapshot Isolation):整個交易看同一份快照(MVCC),防不可重複讀。
- 可序列化(Serializable):最強,行為等同交易一個個依序執行,防所有異常(含寫偏斜 write skew / 幻讀)。實作:真序列化、2PL、或 SSI。
步驟:先想清楚你要防哪種併發異常,再選剛好夠的等級(越強越慢)。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
- 很多預設是「讀已提交」,擋不住 write skew;涉及金額/庫存等不變式要特別確認等級。
- 別在交易內做外部 I/O(如發 MQ)→ 見 工具-Transactional-Outbox-Pattern。
🔗 相關工具
- 工具-線性一致性與共識 —— 把同一個問題拉到分散式:單機的隔離等級到了多節點就變成一致性與共識的取捨
- 工具-防禦式編程 —— 補位:隔離等級擋不掉的併發情境(如應用層的檢查再寫入),要靠這張的心態補上
分散式系統的麻煩 不可靠的網路與時鐘、部分失效、拜占庭故障——節點只能靠訊息推測他人狀態。
🎯 什麼情境該想到我
當你在分散式系統裡糾結「要不要強一致、可用性 vs 一致性怎麼取捨、多節點怎麼對同一件事達成一致」時。
⚙️ 怎麼用
- 線性一致性(Linearizability):讓多副本「看起來像只有一份資料」,讀到的一定是最新已寫入值。強、直覺,但成本高。
- CAP 取捨:網路分區(P)發生時,只能在一致性(C)與可用性(A)間擇一。分區無法避免 → 你在選 CP 還是 AP。
- 共識(Consensus):讓一群節點對某個值達成一致(選主、鎖、原子提交)。用 Raft/Paxos 這類演算法,或直接靠 ZooKeeper/etcd。
- 能不要強一致就別要:多數場景用因果一致性/最終一致性即可,成本低很多。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
- 線性一致性很貴且在分區時傷可用性;先問「這個場景真的需要嗎」。
🔗 相關工具
- 工具-資料複製 —— 問題來源,有了副本才需要煩惱各節點看到的順序一不一致
- 工具-交易與隔離等級 —— 單機對照組,隔離等級解決的是單庫併發,共識解決的是跨節點共識
🌐 第二部:分散式資料
批次處理 MapReduce 與資料流引擎:對有界資料集做離線轉換,產出衍生資料。
串流處理 事件流、變更資料擷取(CDC)、事件溯源:把批次的邊界拿掉,變成持續處理。
🔀 第三部:衍生資料