📌 30 秒摘要(Layer 3)
系統整理大型網站架構的「核心要素與慣用招式」。一個網站要撐住高流量,靠的是圍繞五大要素(性能、可用性、伸縮性、擴展性、安全性)反覆運用九種模式:分層、分割、分散式、叢集、快取、非同步、冗餘、自動化、安全。與 淘寶技術這十年(故事)、鳳凰架構(理論)互補,本書偏「要素清單 + 慣用模式 + 真實案例」。全書 240 頁、幾乎沒有程式碼,作者自稱可當成網站架構設計的科普書。
🗺 心智圖(Canvas)
大型網站技術架構
Link to original 📗 大型網站技術架構
核心原理與案例分析
五大要素 × 九種模式
🧭 總綱
🎯 什麼情境該想到我
當你「面對高並發存取、海量資料處理、高可靠運行的挑戰,需要一份被許多大型網站反覆驗證過的通用招式清單」的時候。
⚙️ 怎麼用(九式 × 五要素的檢查清單)
第一部分:九種架構模式(2.1)
1. 分層(2.1.1) —— 把系統在橫向維度上切成幾個部分,每部分負責相對單一的職責,再透過上層對下層的依賴和呼叫組成完整系統。書中把網站軟體系統分為三層(表 2.1):
層 職責 應用層 負責具體業務和視圖展示,如網站首頁及搜尋輸入和結果展示 服務層 為應用層提供服務支援,如使用者管理服務、購物車服務等 資料層 提供資料儲存存取服務,如資料庫、快取、檔案、搜尋引擎等 好處是便於分工合作開發和維護;各層之間具有一定獨立性,只要維持呼叫介面不變,各層可獨立演化。挑戰是必須合理規劃層次邊界和介面,開發過程中嚴格遵循約束,禁止跨層次呼叫(應用層直接呼叫資料層)及逆向呼叫(資料層呼叫服務層,或服務層呼叫應用層)。大的分層內部還可以繼續分層:應用層可再細分視圖層(美工負責)和業務邏輯層(工程師負責),服務層可細分資料介面層和邏輯處理層。分層架構是邏輯上的,三層可以部署在同一台機器,隨業務發展再分離部署到不同伺服器。因此在網站規模還很小的時候就應該採用分層架構,將來做大才好應對。
2. 分割(2.1.2) —— 如果分層是橫向切分,分割就是在縱向方面切分:把不同的功能和服務分割開來,包裝成高內聚低耦合的模組單元。書中的例子:在應用層把購物、論壇、搜尋、廣告分割成不同的應用,由獨立團隊負責、部署在不同伺服器上;同一個應用內部若規模龐大,購物業務可再分割成機票酒店業務、3C 業務、小商品業務;即使在這個粒度上,還可以繼續分割成首頁、搜尋列表、商品詳情等模組,這些模組不管邏輯上還是實體部署上都可以是獨立的。服務層同樣可依需要分割成合適的模組。
3. 分散式(2.1.3) —— 分層和分割的一個主要目的,就是讓切分後的模組便於分散式部署,把不同模組部署在不同伺服器上,透過遠端呼叫協同工作。書中列出的常用方案:
- 分散式應用和服務:改善效能和並發性、加快開發和發布速度、減少資料庫連線資源消耗,還可以讓不同應用複用共同的服務,便於業務功能擴展。
- 分散式靜態資源:JS、CSS、Logo 圖片等靜態資源獨立分散式部署,並採用獨立的域名,即所謂動靜分離。可減輕應用伺服器負載壓力,用獨立域名加快瀏覽器並發載入速度,並由負責使用者體驗的團隊開發維護。
- 分散式資料和儲存:大型網站要處理以 P 為單位的海量資料,單台電腦無法提供如此大的儲存空間。除了對傳統關聯式資料庫做分散式部署外,為網站應用而生的各種 NoSQL 產品幾乎都是分散式的。
- 分散式計算:搜尋引擎的索引建構、資料倉儲的資料分析統計等後台批次處理,目前普遍使用 Hadoop 及其 MapReduce 分散式計算框架,其特點是移動計算而不是移動資料——把計算程式分發到資料所在的位置以加速計算。
- 此外還有支援線上伺服器設定即時更新的分散式設定、分散式環境下實現並發與協同的分散式鎖、支援雲端儲存的分散式檔案系統。
書中警告分散式帶來的四個問題:①服務呼叫必須經過網路,可能嚴重影響效能;②伺服器越多,宕機機率越大,一台宕機可能導致很多應用無法存取,降低可用性;③分散式環境中保持資料一致性非常困難,分散式交易也難以保證;④導致網站依賴錯綜複雜,開發管理維護困難。所以「分散式設計要根據具體情況量力而行,切莫為了分散式而分散式」。
4. 叢集(2.1.4) —— 對於使用者存取集中的模組(比如網站首頁),還需要把獨立部署的伺服器叢集化:多台伺服器部署相同應用構成一個叢集,透過負載平衡設備共同對外提供服務。好處是並發特性更好(要更多容量只需向叢集中加入新機器),而且某台伺服器故障時,負載平衡設備或系統的失效轉移機制會把請求轉發到叢集中其他伺服器,故障不影響使用者使用。書中明講:即使是存取量很小的分散式應用和服務,也至少要部署兩台伺服器構成一個小的叢集,目的就是提高系統的可用性。
5. 快取(2.1.5) —— 把資料存放在距離計算最近的位置以加快處理速度;快取是改善軟體效能的第一手段。四個位置:
- CDN(內容分發網路):部署在距離終端使用者最近的網路服務商,快取網站的一些靜態資源(較少變化的資料),就近以最快速度回傳;影音網站和入口網站會把使用者存取量大的熱點內容快取在 CDN。
- 反向代理:屬於網站前端架構的一部分,部署在網站前端,使用者請求到達資料中心時最先存取到它,這裡快取靜態資源,無需轉發給應用伺服器就能回傳。
- 本地快取:在應用伺服器本地快取熱點資料,應用程式直接存取本機記憶體,無需存取資料庫。
- 分散式快取:資料量龐大到單機記憶體承受不住,把資料快取在專門的分散式快取叢集中,應用程式透過網路通訊存取。
使用快取的兩個前提條件:①資料存取熱點不均衡,某些資料會被更頻繁地存取,這些資料應該放在快取中;②資料在某個時間段內有效、不會很快過期,否則快取的資料會因失效而產生髒讀,影響結果正確性。快取除了加快存取速度,還能減輕後端應用和資料儲存的負載壓力——這點對網站資料庫架構至關重要,網站資料庫幾乎都是按照有快取的前提來設計負載能力的。
6. 非同步(2.1.6) —— 降低耦合的重要手段:業務之間的訊息傳遞不是同步呼叫,而是把一個業務操作分成多個階段,各階段之間透過共享資料的方式非同步執行協作。單機內部可透過多執行緒共享記憶體佇列實現;分散式系統中,多個伺服器叢集透過分散式訊息佇列實現非同步,分散式訊息佇列可看作記憶體佇列的分散式部署。非同步架構是典型的生產者消費者模式,兩者不存在直接呼叫,只要保持資料結構不變,彼此實現可隨意變化。除此之外還有三個特性:
- 提高系統可用性:消費者伺服器故障時,資料會在訊息佇列伺服器中堆積,生產者可以繼續處理業務請求,系統整體表現無故障;消費者恢復後繼續處理。
- 加快網站回應速度:生產者處理完業務請求後把資料寫入訊息佇列即可回傳,不需等待消費者處理,回應延遲減少。
- 消除並發存取高峰:促銷活動、熱點事件會造成並發存取突然增大,用訊息佇列把突增的請求放進去等消費者依次處理,就不會對整體負載造成太大壓力。
提醒:使用非同步方式處理業務可能會對使用者體驗、業務流程造成影響,需要網站產品設計方面的支援。
7. 冗餘(2.1.7) —— 網站需要 7×24 小時連續運行,但伺服器隨時可能故障,規模大時宕機是必然事件。要在宕機時仍能繼續服務、不丟資料,就需要一定程度的伺服器冗餘運行與資料冗餘備份,宕機時把其上的服務和資料存取轉移到其他機器。存取和負載很小的服務也必須部署至少兩台伺服器構成叢集。資料庫除了定期備份、存檔保存實現冷備份外,為保證線上業務高可用,還需要主從分離、即時同步實現熱備份。為了抵禦地震、海嘯等不可抗力導致的完全癱瘓,某些大型網站會對整個資料中心進行備份,在全球範圍內部署災備資料中心,程式和資料即時同步到多個災備資料中心。
8. 自動化(2.1.8) —— 目前大型網站的自動化架構設計主要集中在發布運維方面。發布環節出的故障最多,減少人為干預可有效減少故障:
- 自動化程式碼管理:版本控制、分支建立合併等自動化,工程師只要提交參與開發的產品代號,系統就自動建立開發分支,後期自動合併。
- 自動化測試:提交測試後系統自動部署到測試環境,啟動自動化測試案例,發送測試報告並向系統回饋結果。
- 自動化安全檢測:透過靜態安全掃描及部署到安全測試環境進行攻擊測試,評估安全性。
- 自動化部署:把工程程式碼自動部署到線上生產環境。
運行期則有:自動化監控(心跳檢測、監控效能指標與應用關鍵資料指標)、自動化報警(超出預設閾值就向相關人員發送警訊)、自動化失效轉移(把失效伺服器從叢集隔離出去)、自動化失效恢復(故障消除後重啟服務、同步資料保證一致性)、自動化降級(存取高峰超出最大處理能力時,拒絕部分請求及關閉部分不重要的服務,把負載降到安全水平)、自動化分配資源(把閒置資源分配給重要的服務,擴大其部署規模)。
9. 安全(2.1.9) —— 書中列出的手法:透過密碼和手機校驗碼進行身分認證;登入、交易等操作需要對網路通訊加密,伺服器上儲存的敏感資料(如使用者資訊)也做加密處理;為防止機器人程式濫用網路資源攻擊網站,使用驗證碼識別;對於常見的 XSS 攻擊、SQL 注入,進行編碼轉換等相應處理;對垃圾資訊、敏感資訊進行過濾;對交易轉帳等重要操作根據交易模式和交易資訊進行風險控制。
第二部分:五大核心架構要素(第 3 章)
除了當前的系統功能需求外,軟體架構還需要關注性能、可用性、伸縮性、擴展性、安全性這 5 個架構要素;架構設計過程中需要平衡這 5 個要素之間的關係,也可以透過考察這些要素來衡量一個軟體架構設計的優劣。
性能(3.1) —— 使用者無法忍受回應緩慢的網站,打開緩慢會導致嚴重的使用者流失;很多時候性能問題是網站架構升級最佳化的觸發器。從瀏覽器到資料庫,影響使用者請求的所有環節都可以最佳化:
- 瀏覽器端:瀏覽器快取、頁面壓縮、合理布局頁面、減少 Cookie 傳輸。
- CDN:把靜態內容分發至離使用者最近的網路服務商機房,用最短存取路徑取得資料。
- 反向代理:在網站機房部署,快取熱點檔案,加快回應、減輕應用伺服器負載。
- 應用伺服器端:本地快取與分散式快取,用記憶體中的熱點資料處理請求。
- 非同步操作:把請求送到訊息佇列等待後續處理,當前請求直接回傳回應。
- 叢集:多台應用伺服器組成叢集共同對外服務,提高整體處理能力。
- 程式碼層面:多執行緒、改善記憶體管理。
- 資料庫端:索引、快取、SQL 最佳化等手段都已比較成熟;NoSQL 則透過最佳化資料模型、儲存結構、伸縮特性等在效能上的優勢日趨明顯。
衡量指標:回應時間、TPS、系統性能計數器。這些指標也是網站監控的重要參數,可用來分析系統瓶頸、預測網站容量、對異常指標報警。性能符合預期只是必要條件——還必須考察超出負載設計能力的高並發情況下可能出現的性能問題,並保證長時間持續運行且存取壓力不均勻時仍保持穩定的性能特性。
可用性(3.2) —— 網站宕掉輕則影響聲譽,重則損失金錢與使用者。幾乎所有網站都承諾 7×24 可用,但沒有網站能做到完全的 7×24;扣除故障時間換算成可用性指標,一些知名大型網站可以做到4 個 9 以上,也就是可用性超過 99.99%。因為網站用的是普通商用伺服器,設計目標本身不保證高可用,大型網站通常有上萬台伺服器、每天必定有一些宕機,所以高可用架構設計的前提是「必然會出現伺服器宕機」,目標是宕機時服務或應用依然可用。
- 主要手段是冗餘:應用部署在多台伺服器上同時提供存取,資料儲存在多台伺服器上互相備份。
- 應用伺服器:多台透過負載平衡設備組成叢集,任一台宕機只需把請求切換到其他伺服器;前提是應用伺服器上不能保存請求的工作階段(session)資訊,否則宕機會話丟失,轉發也無法完成業務處理。
- 儲存伺服器:需要對資料即時備份,宕機時把資料存取轉移到可用伺服器,並進行資料恢復,以保證繼續有伺服器宕機時資料依然可用。
- 除了運行環境,高可用還需要軟體開發過程的品質保證:預發布驗證、自動化測試、自動化發布、灰度發布,減少把故障引入線上環境的可能。
衡量標準:假設系統中任何一台或多台伺服器宕機、以及出現各種不可預期的問題時,系統整體是否依然可用。
伸縮性(3.3) —— 定義:透過不斷向叢集中加入伺服器的手段,來緩解不斷上升的使用者並發存取壓力和不斷增長的資料儲存需求。衡量標準:是否可以用多台伺服器建構叢集、是否容易向叢集中添加新伺服器、加入後是否能提供和原伺服器無差別的服務、叢集中可容納的伺服器總數是否有限制。
- 應用伺服器叢集:只要伺服器上不保存資料,所有伺服器都是對等的,用合適的負載平衡設備就可以不斷加入伺服器。
- 快取伺服器叢集:加入新伺服器可能導致快取路由失效,進而使叢集中大部分快取資料無法存取;雖然可從資料庫重新載入,但若應用已嚴重依賴快取,可能導致整個網站崩潰,需要改進快取路由演算法保證快取資料的可存取性。
- 關聯式資料庫:雖支援資料複製、主從熱備等機制,但很難做到大規模叢集的可伸縮性,伸縮性方案必須在資料庫之外實現,透過路由分區等手段把部署有多個資料庫的伺服器組成叢集。
- NoSQL:先天為海量資料而生,對伸縮性的支援通常非常好,可在較少運維參與下實現叢集規模的線性伸縮。
擴展性(3.4) —— 不同於其他架構要素主要關注非功能性需求,擴展性直接關注網站的功能需求:如何設計架構使其能快速回應需求變化。衡量標準:增加新的業務產品時,是否可以對現有產品透明無影響,不需要任何改動或很少改動既有業務功能就能上線新產品;不同產品之間是否很少耦合,一個產品改動對其他產品無影響。主要手段是事件驅動架構和分散式服務(書中此處原文寫作「網站可伸縮架構的主要手段」,依上下文屬 3.4 擴展性一節):
- 事件驅動架構:通常利用訊息佇列實現,把使用者請求和其他業務事件構造成訊息發布到訊息佇列,處理者作為消費者取得訊息處理。訊息產生與訊息處理分離,可以透明地增加新的訊息生產者任務或新的訊息消費者任務。
- 分散式服務:把業務和可複用服務分離開來,透過分散式服務框架呼叫。新增產品可透過呼叫可複用服務實現自身業務邏輯,對現有產品沒有任何影響;可複用服務升級變更時,也可以提供多版本服務對應用實現透明升級,不需要強制應用同步變更。
- 大型網站為保持市場地位還會吸引第三方開發者呼叫網站服務、使用網站資料開發周邊產品,主要途徑是開放平台介面。
安全性(3.5) —— 網站的安全架構就是保護網站不受惡意存取和攻擊,保護網站的重要資料不被竊取。衡量標準:針對現存和潛在的各種攻擊與竊密手段,是否有可靠的應對策略。(書中本節僅此兩句層級的內容,具體手法見 2.1.9 安全模式。)
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 書中 2.1.3 明講:「分散式設計要根據具體情況量力而行,切莫為了分散式而分散式。」分散式會帶來網路效能損耗、宕機機率上升、資料一致性困難、依賴錯綜複雜四個代價,不要把它當成預設答案。
- 2.1.6:非同步可能影響使用者體驗與業務流程,需要產品設計方面的支援,不是純技術決定。
- 2.1.5:快取有兩個前提(熱點不均衡、時間段內有效),不成立時快取只會帶來髒讀。
- 2.3 小結:「模式受其適用場景限制,對系統的要求和約束也很多,不恰當地使用模式只會畫虎不成反類犬」;好的設計不是生搬硬套某個模式,而是對問題深刻理解之上的創造與創新。
🔗 相關工具
- 工具-網站高可用與伸縮性設計 —— 五要素中可用性與伸縮性的展開做法
- 工具-透明多級分流 —— 對應快取模式的 CDN/反向代理/本地快取分層落地
- 工具-快取與資源優化 —— 快取模式的細部策略與失效處理
- 工具-資料分區 —— 關聯式資料庫伸縮性必須在資料庫之外實現的路由分區手段
- 工具-資料複製 —— 冗餘模式中主從分離、即時同步熱備份的機制
- 工具-訊息傳遞整合模式 —— 非同步與事件驅動架構所依賴的訊息佇列
- 工具-架構隨業務規模演進 —— 這九式該在什麼階段依序引入
- 工具-可靠可擴展可維護 —— 另一套核心架構要素的說法,可交叉對照
- 工具-系統設計思考框架 —— 設計新系統時搭配使用的整體流程
- 工具-管理複雜度 —— 分層分割的根本目的:控制複雜度
- 大型網站技術架構 —— 來源書
🎯 什麼情境該想到我
當你要評估或設計一個資料/後端系統,想有一套「該顧哪些面向」的總框架時。
⚙️ 怎麼用(三個評估軸)
- 可靠性(Reliability):即使硬體/軟體/人為出錯,仍能正確運作。→ 設計容錯,主動注入故障測試。
- 可擴展性(Scalability):先定義負載參數(QPS、讀寫比、資料量),再看負載成長時效能如何。用百分位(p95/p99)談延遲,別只看平均。
- 可維護性(Maintainability):可運維性、簡單性(控制複雜度)、可演進性。
面對任何系統,逐軸問一遍,就不會漏掉非功能面。
★ 可靠性:三種故障,三種應對
故障類型 例子 ★ 應對 硬體 磁碟壞、機器當、斷電 冗餘(複製、多可用區)——這是最容易解的一種 軟體 bug、記憶體洩漏、失控的重試 ★ 難得多:相關聯的故障會同時發生在所有節點上 ★ 人為 設定錯誤、誤操作 ★ 統計上最大的故障來源 ★ 「人為是最大來源」這一點最常被忽略。對策不是「叫人更小心」,而是:
- 讓正確的事容易做、錯的事難做(好的預設值、危險操作要多一道確認)
- 沙箱環境讓人可以安全地試
- ★ 快速回滾比「不要出錯」更實際(見 工具-低風險發布模式)
- 完整的遙測(見 工具-遙測與監控)
★ 可擴展性:必須先定義「負載參數」
「這個系統可擴展嗎」是一個無法回答的問題。 要先問:
① 負載參數是什麼? ★ 不同系統的關鍵參數完全不同: Web 服務 → QPS 資料庫 → 讀寫比 聊天室 → 同時在線人數 ★ Twitter → 「每個使用者的追蹤者數分布」(不是總推文數) ② 負載增加時,系統怎麼變? 固定資源下,效能降低多少? 要維持效能,資源要增加多少?★ Twitter 的例子值得記住:
瓶頸不是「每秒幾則推文」,而是**「名人發一則推文要寫進幾百萬個時間線」**——
找對負載參數,才找得到真正的瓶頸。 找錯了,優化全部做在沒用的地方。★★ 延遲一定要用百分位,不能用平均
100 個請求:99 個 10ms,1 個 5000ms 平均 = 59.9ms ← ★ 這個數字騙了你 p99 = 5000ms ← ★ 這才是「最糟的那 1% 使用者」的體感
為什麼 說明 ★ 平均會被長尾拉走,但又掩蓋長尾 它既不代表典型,也不代表最糟 ★ 尾延遲放大(tail latency amplification) 一個請求要呼叫 10 個後端,只要有一個慢,整個請求就慢。
後端 p99 = 1%,則前端「至少碰到一個慢的」機率 ≈ 9.6%——尾巴被放大了 10 倍★ 最重要的使用者常在尾巴 資料最多的往往是最有價值的客戶 要看的指標:p50(典型)、p95、p99、p999、max。
★ 而且百分位不能平均(三台機器的 p99 各是 100ms ≠ 整體 p99 是 100ms)——
要用直方圖或 t-digest 合併(見 順序統計量與中位數)。★ 可擴展的兩個方向
垂直(scale up) 水平(scale out) 做法 換更大的機器 加更多機器 複雜度 ★ 低(什麼都不用改) 高(分區、複製、一致性) 上限 有物理上限 幾乎無限 建議 ★ 先垂直。單機現在可以很大(TB 記憶體、上百核心) 垂直撐不住才水平 ★ 「無狀態服務水平擴展很容易,有狀態的很難」——
所以架構設計的一個核心目標是把狀態集中到少數地方(資料庫、快取),讓其他部分無狀態。★ 可維護性:三個子維度
維度 問什麼 可運維性 ★ 出事時能不能快速知道、快速定位、快速修?(遙測、runbook、可回滾) 簡單性 ★ 新人多久能上手?複雜度是本質的還是偶然的?(見 工具-管理複雜度) 可演進性 需求變了,改起來容易嗎? ★ 「偶然複雜度」是可以被消除的(Brooks 的區分):
本質複雜度來自問題本身;偶然複雜度來自我們的解法——
工具爛、抽象錯、歷史包袱。可維護性的工作幾乎都在消除偶然複雜度。★ 怎麼實際使用這三軸
在設計 review 或系統設計面試時,逐軸問一遍:
可靠性 → 哪裡會掛?掛了會怎樣?人為失誤怎麼防?能多快回滾? 可擴展性 → 負載參數是什麼?10 倍時哪裡先爆?p99 是多少? 可維護性 → 誰來維運?出事怎麼查?新人要多久上手?★ 這三個問題涵蓋了幾乎所有「非功能需求」,而它們正是系統設計評審真正在看的東西
(見 AI 系統設計框架、工具-系統設計思考框架)。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★ 三者互相衝突。更可靠通常更慢更貴;更可擴展通常更複雜(更難維護)。
這是取捨不是清單——要明說你選了哪一邊、為什麼。- ★ 不要過早追求可擴展性。「未來可能要撐一百萬使用者」是最常見的過度設計理由。
先做能跑的簡單系統,量出真正的瓶頸再擴展——而且那時你才知道正確的負載參數。- 可靠性有邊際成本。從 99% 到 99.9% 不貴;從 99.99% 到 99.999% 可能要十倍投入。
★ 先問業務需要多少個 9,不要預設越高越好。- ★ 平均值幾乎永遠是錯的指標(見上)。
- 可維護性最難量化,所以最容易被犧牲。→ 用可觀測的代理指標:
DORA 的交付指標:吞吐量(變更前置時間、部署頻率、失敗部署的恢復時間)與不穩定度(變更失敗率、部署重工率),共五項;「失敗部署的恢復時間」取代了早期的 MTTR(查證 2026-09-25,見 工具-部署管線與持續交付)。- 「可擴展的架構」不存在,只有「針對某種負載可擴展」。負載模式變了就要重新設計。
🔗 相關工具
- 工具-系統設計思考框架 —— 怎麼把這三軸用在實際設計上
- 工具-容量估算 —— 可擴展性的量化
- 工具-管理複雜度 —— 可維護性的核心
- 工具-服務容錯設計 —— 可靠性的具體手段
- 工具-遙測與監控 —— 可運維性的基礎
- 順序統計量與中位數 —— ★ 百分位怎麼算、為什麼不能平均
- 工具-用度量取代模糊判斷 —— DORA 四指標
- 設計資料密集型應用
⚡ 性能
🎯 什麼情境該想到我
當使用者「重複造訪網站卻還是慢」、資源每次都重新下載時。
⚙️ 怎麼用
- 設快取標頭:對靜態資源加長
Cache-Control/Expires,讓瀏覽器重複造訪直接用本地副本。- 內容雜湊檔名(cache busting):檔名帶 hash(如
app.9f3c.js),內容一改檔名就變 → 可放心設「永久快取」,更新自動失效。(本站 Quartz 就是這樣。)- 善用多層快取:瀏覽器 → CDN → 反向代理 → 應用快取,越外層命中越省。
- 壓縮與合適格式:圖片用適當格式/尺寸,別傳過大資源。
★
Cache-Control的五個關鍵指令
指令 意思 常見誤解 max-age=NN 秒內直接用快取,不問伺服器 no-cache可以存,但每次使用前都要向伺服器驗證 常被誤以為是「不要快取」 no-store完全不要存 真正的「不要快取」;用在含個資的回應 immutable在 max-age 期間內容絕不會變,連重新整理都不用驗證 只能搭配內容雜湊檔名使用 private/public只能存在瀏覽器/CDN 等共用快取也可以存 含使用者資料的回應一定要 private★ 兩種資源、兩種策略
資源 例子 標頭 更新怎麼生效 帶雜湊的靜態檔 app.9f3c2a.js、style.a81b.cssCache-Control: public, max-age=31536000, immutable內容改變 → 檔名改變 → 等於新檔案 入口 HTML(和其他不能改名的) index.htmlCache-Control: no-cache+ETag每次驗證;沒變就回 304 Not Modified(不傳內容) 這個組合的邏輯:HTML 每次都驗證(很小,304 只要一個往返),HTML 裡引用的帶雜湊檔名則永久快取。部署新版本時,新的 HTML 指向新的檔名,舊檔案自然不再被使用。不需要手動清快取。
★ 驗證的機制:ETag 與 304
- 第一次請求:伺服器回
200+ETag: "abc123"。- 之後的驗證:瀏覽器送
If-None-Match: "abc123"。- 內容沒變:伺服器回
304 Not Modified,沒有 body → 省下傳輸量,但仍然有一個往返。- 內容變了:回
200+ 新內容 + 新 ETag。所以
no-cache+ ETag 省的是頻寬,max-age省的是往返。行動網路上一個往返可能要幾百毫秒,這就是為什麼帶雜湊的資源要用max-age+immutable。★ Go 的做法
// 帶雜湊的靜態檔:永久快取 fs := http.FileServerFS(assets) // Go 1.22+;assets 是 embed.FS 或 os.DirFS mux.Handle("GET /assets/", http.StripPrefix("/assets/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") fs.ServeHTTP(w, r) })))
http.FileServer會自動處理If-Modified-Since/Last-Modified,並在內容沒變時回 304。- 但用
embed.FS嵌入檔案時,檔案的修改時間是零值,Last-Modified機制不會生效 → 靠檔名雜湊 +immutable,或自己計算 ETag。★ 多層快取的失效順序
瀏覽器 → CDN → 反向代理 → 應用程式快取 → 資料庫。失效要從內往外:先更新資料庫,再清應用程式快取,最後清 CDN。反過來做,外層清完後可能馬上又從內層拿到舊資料再快取一次。
stale-while-revalidate=N:過期後的 N 秒內,先回傳舊的快取給使用者,同時在背景重新驗證——使用者不用等,資料最多舊 N 秒。適合「稍微舊一點沒關係」的內容。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
快取最難的是「失效」;用內容雜湊檔名可一併解掉「更新了卻還吃到舊檔」的問題。
CDN 快取了錯誤回應:後端短暫出錯回了 500 或空白頁,如果沒有正確的
Cache-Control,CDN 可能快取它並發給所有使用者。錯誤回應要明確設no-store。
Vary標頭:同一個網址會依照Accept-Encoding、Accept-Language回不同內容時,要設Vary,否則 CDN 可能把 gzip 版本發給不支援的客戶端,或把中文版發給英文使用者。🔗 相關工具
- 工具-前端效能優化規則 —— 快取只是其中一條規則;跳過去拿完整清單,判斷該先動快取還是先減請求數
- 工具-透明多級分流 —— 多層快取的上游架構:瀏覽器 → CDN → 反向代理這條路徑本身該怎麼擺
🛡 可用性
🎯 什麼情境該想到我
當你要讓網站/服務「扛住高流量、少當機、能加機器就變強」時。
⚙️ 怎麼用(兩大目標的核心手法)
高可用(少當機):
- 消除單點:關鍵元件都要有冗餘備援 + 自動故障切換。
- 服務無狀態:狀態外置(快取/DB),任一台掛了流量可轉到別台。
- 故障隔離:艙壁、限流、降級(見 工具-服務容錯設計),別讓局部拖垮全局。
伸縮性(能水平擴展):
4. 無狀態 + 負載均衡 → 加機器就能分攤(見 工具-透明多級分流)。
5. 資料層靠分片 + 讀寫分離(見 工具-資料分區、工具-資料複製)。
6. 非同步削峰:用訊息佇列把尖峰流量緩衝、削峰填谷。★ 先算清楚「幾個 9」的真實意義
SLA 每年可停機 每月 代價 99% 3.65 天 7.2 小時 低 99.9% 8.8 小時 43 分鐘 中 99.95% 4.4 小時 22 分鐘 ★ 多數 SaaS 的實際目標 99.99% 52 分鐘 4.3 分鐘 ★ 高(要多可用區 + 自動故障轉移) 99.999% 5.3 分鐘 26 秒 ★ 極高(幾乎沒有人工介入的餘地) (「每月」以 30 天 = 43,200 分鐘計:99.9% → 43.2 分鐘、99.99% → 4.32 分鐘;若用平均月長 365/12 ≈ 30.44 天 ≈ 730 小時,則是 43.8、4.38 分鐘,即 工具-容量估算 表上的數字。)
★ 99.99% 代表「一個月只有 4 分鐘」——
這比「人被叫醒 + 登入 + 判斷 + 處理」還短。
所以四個 9 以上幾乎必然要求「全自動故障轉移」,而那本身就是一個複雜且會出錯的系統。★ 先問業務要幾個 9,不要預設越高越好——每多一個 9,成本大約是十倍。
★ 可用性是「相乘」的,不是「取最小」
服務 A(99.9%)同步呼叫 B(99.9%)與 C(99.9%) ★ 整體 = 0.999³ = 99.7% ← 比任何單一元件都差 ★ 每增加一個同步依賴,可用性都在下降。這推出三條設計原則:
原則 說明 ★ 減少同步依賴 能非同步就非同步(見 工具-訊息傳遞整合模式) ★ 讓依賴可選 推薦服務掛了仍能結帳(降級,見 工具-服務容錯設計) 冗餘讓可用性相加 兩個副本並聯:1−(1−0.999)2=99.9999% ★ 找單點的系統化方法
逐項問「這個掛了會怎樣」:
□ 負載均衡器本身 → 多台 + VIP/DNS 故障轉移 □ ★ 資料庫主庫 → 最常見的單點。需要 failover(見 [[工具-資料複製]]) □ 快取 → 掛了會不會把流量全打到 DB?(★ 快取雪崩) □ ★ 訊息佇列 → 叢集化 □ ★ 設定中心 / 服務發現 → 掛了服務還起得來嗎?要有本地快取 □ 物件儲存 / CDN → 通常是託管的,但要看 SLA □ ★ 憑證 / DNS 到期 → ★ 最常被忘記的單點,而且會全站掛掉 □ ★ 單一可用區 → 整個機房掛掉 □ ★ 部署流程本身 → CI 掛了能不能緊急上線? □ ★ 人 → 只有一個人會處理某件事★ 最後三項最常被忽略,而且它們造成的事故最久。
★ 無狀態為什麼是可擴展的前提
有狀態(session 存在記憶體) → 使用者必須回到同一台(sticky session) → ★ 那台掛了,使用者的狀態就沒了 → ★ 無法自由擴縮(加機器不會分擔既有使用者) → 部署要考慮 draining 無狀態(session 存 Redis / JWT) → ★ 任何一台都能處理任何請求 → 加機器立刻分擔、掛一台無感、部署可以直接滾動★ 「把狀態集中到少數地方」是架構設計的核心手法:
應用層無狀態(容易擴展)+ 狀態集中在資料庫/快取(難擴展但只有一處要處理)。★ 削峰填谷:非同步化的三種形態
形態 做什麼 適合 佇列緩衝 收下請求先回 202,背景處理 ★ 不需要即時結果的(寄信、產報表、轉檔) ★ 批次合併 把 N 個小寫入合併成一次大寫入 高頻小寫入(計數、日誌) ★ 限流 + 排隊 超過容量就排隊或拒絕 秒殺、搶票(見 工具-秒殺系統設計) ★ 關鍵:佇列不是無限的緩衝。
生產者長期快於消費者 → 佇列無限成長 → 記憶體/磁碟爆掉,而且延遲會變得極長。
佇列只能吸收「尖峰」,不能解決「容量不足」。
→ 一定要有有界佇列 + 背壓 + 監控佇列長度(見 工具-MQ消費端防禦三原則)。★ 可用性的敵人往往不是硬體
真實的停機原因 對策 ★ 部署出錯 金絲雀 + 快速回滾(見 工具-低風險發布模式) ★ 設定變更 設定也要走 review 與灰度 ★ 憑證/網域過期 自動續期 + 到期告警 ★ 依賴的第三方掛了 降級路徑 流量尖峰 限流 + 自動擴縮 硬體故障 冗餘(這是最容易解的一種) ★ 統計上,「變更」是絕大多數事故的觸發點——
所以提升可用性最有效的單一手段,是「讓變更更安全」而不是「加更多冗餘」。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★ 自動故障轉移本身會製造事故。誤判、腦裂、轉移到一個也壞的節點。
很多成熟團隊選擇半自動(自動偵測 + 人工確認)——錯誤的 failover 比晚幾分鐘恢復更糟
(見 工具-資料複製、工具-故障偵測)。- ★ 冗餘要真的獨立。兩台機器在同一個機架、同一個電源、同一個可用區 → 不是冗餘。
- ★ 快取掛掉要能撐住。快取雪崩(大量 key 同時過期或快取整個掛)會讓流量瞬間全打到 DB。
→ TTL 加隨機抖動、快取掛了要限流保護 DB。- 演練過的才算有。沒演練過的 failover 在真正需要時多半會失敗(見 工具-注入故障與韌性演練、遊戲日)。
- ★ SLA 要包含「誰來承諾什麼」。99.99% 不含計畫性維護?不含第三方故障?這些都要寫清楚。
- 備份 ≠ 高可用。備份解決「資料遺失」,高可用解決「服務中斷」。兩者都要,而且備份要定期演練還原。
🔗 相關工具
- 工具-服務容錯設計 —— 降級、熔斷、艙壁的具體做法
- 工具-透明多級分流 —— 流量層的冗餘
- 工具-資料複製 —— 資料層的冗餘與 failover 的坑
- 工具-故障偵測 —— 自動轉移的前提與誤判風險
- 工具-低風險發布模式 —— ★ 變更是最大的事故來源
- 工具-注入故障與韌性演練、遊戲日 —— 演練過的才算有
- 工具-MQ消費端防禦三原則 —— 削峰的背壓
- 工具-秒殺系統設計 —— 極端尖峰的完整案例
🎯 什麼情境該想到我
當你「覺得這種小事不會出問題」的時候——高併發與海量資料下,很多平常根本不是問題的問題都會冒出來。
⚙️ 怎麼用(九個真實故障 × 教訓)
寫日誌也會引發故障 —— 現象:某應用伺服器叢集發布後不久,多台伺服器相繼報警,硬碟可用空間低於警戒值,很快有伺服器宕機;登入線上伺服器發現 log 資料夾檔案迅速增加,不斷吃掉磁碟空間。/原因:這是普通應用伺服器叢集,不需要儲存資料,因此配的是一塊 100GB 的小硬碟,裝完作業系統、Web 伺服器、Java 虛擬機、應用程式後只剩幾十 GB,正常情況夠用;但開發人員把 log 輸出的 level 全域配置為 Debug,一次簡單的 Web 請求就產生大量 log 輸出,高併發下很快耗盡磁碟。/教訓:應用程式自己的日誌輸出配置和第三方元件日誌輸出要分別配置;檢查 log 配置檔,日誌輸出級別至少為 Warn,並檢查 log 輸出的程式碼呼叫,呼叫級別要符合其真實日誌級別;有些開源第三方元件也會不恰當地輸出太多 Error 日誌,需要關掉這些第三方庫的日誌輸出——至於哪些第三方庫有問題,只有在遇到問題時才知道。
高併發存取資料庫引發的故障 —— 現象:某應用發布後,資料庫 Load 居高不下,遠超過正常水準,持續報警。/原因:檢查資料庫,發現報警是某條 SQL 引起的;這條 SQL 只是一條簡單的、有索引的資料查詢,本來不該引發報警。繼續查,發現這條 SQL 執行頻率非常高,遠遠超過正常水準;追查下去,發現它被網站首頁應用呼叫——首頁是被存取最頻繁的網頁,這條 SQL 被首頁呼叫,也就被頻繁執行了。/教訓:首頁最好是靜態的;首頁不應該存取資料庫,首頁需要的資料可以從快取伺服器或者搜尋引擎伺服器獲取。
高併發情況下鎖引發的故障 —— 現象:某應用伺服器不定時因為回應逾時而報警,但很快又逾時解除、恢復正常,如此反覆,讓維運人員非常苦惱。/原因:程式中某個單例物件(singleton object)裡多處使用了 synchronized(this),由於 this 物件只有一個,所有併發請求都要排隊獲得這唯一的一把鎖。一般情況下都是簡單操作,獲得鎖、迅速完成、釋放鎖,不會引起執行緒排隊;但某個需要遠端呼叫的操作也被加了 synchronized(this),這個操作只是偶爾被執行,但每次執行都需要較長時間才能完成,這段時間鎖被佔用,所有使用者執行緒都要等待,於是回應逾時;等這個操作執行完釋放鎖,其他執行緒迅速執行,逾時就解除了。/教訓:使用鎖操作要謹慎。
快取引發的故障 —— 現象:沒有新應用發布,但資料庫伺服器突然 Load 飆升,並很快失去回應;DBA 把資料庫存取切換到備機,Load 也很快飆升並失去回應,最終引發網站全部癱瘓。/原因:快取伺服器在網站伺服器叢集中地位一直比較低,伺服器配置和管理級別都比其他伺服器低一些。大家都認為快取是改善效能的手段,丟失一些快取也沒什麼問題,有時候關閉一兩台快取伺服器也確實對應用沒有明顯影響,所以長期疏於管理快取伺服器。結果這次一個缺乏經驗的工程師關閉了快取伺服器叢集中全部十幾台 Memcached 伺服器,導致網站全部癱瘓的重大事故。/教訓:當快取已經不僅僅是改善效能、而是成為網站架構不可或缺的一部分時,對快取的管理就需要提高到和其他伺服器一樣的級別。
應用啟動不同步引發的故障 —— 現象:某應用發布後,伺服器立即崩潰。/原因:應用程式 Web 環境使用 Apache + JBoss 的模式,使用者請求透過 Apache 轉發給 JBoss。發布時 Apache 和 JBoss 同時啟動,由於 JBoss 啟動時需要載入很多應用並初始化,花費時間較長,結果 JBoss 還沒完全啟動,Apache 就已經啟動完畢開始接收使用者請求,大量請求阻塞在 JBoss 行程中,最終導致 JBoss 崩潰。網站還有很多類似場景,都需要後台服務準備好、前台應用才能啟動,否則就會導致故障——這種情況被內部人戲稱作「姑娘們還沒穿好衣服,老鴇就開門迎客了」。/教訓:老鴇開門前要檢查一下姑娘們是否穿好了衣服。就本例來說,在應用程式中加入一個特定的動態頁面(比如只回傳 OK 兩個字母),啟動腳本先啟動 JBoss,然後在腳本中不斷用 curl 命令存取這個特定頁面,直到收到 OK,才啟動 Apache。
大檔案讀寫獨佔磁碟引發的故障 —— 現象:某應用主要功能是管理使用者圖片,接到部分使用者投訴,表示上傳圖片非常慢,原來只需要一兩秒,現在需要幾十秒,有時等半天結果瀏覽器顯示伺服器逾時。/原因:圖片需要使用儲存,最可能出錯的地方是儲存伺服器。檢查儲存伺服器,發現大部分檔案只有幾百 KB,而有幾個檔案非常大、有數百 MB,讀寫這些大檔案一次需要幾十秒,這段時間磁碟基本被這個檔案操作獨佔,導致其他使用者的檔案操作緩慢。/教訓:儲存的使用需要根據不同檔案類型和用途進行管理,圖片都是小檔案,應該使用專用的儲存伺服器,不能和大檔案共用儲存;批次處理用的大檔案可以使用其他類型的分散式檔案系統。
濫用生產環境引發的故障 —— 現象:監控發現某個時段內,某些應用突然變慢,內部網路存取延遲非常厲害。/原因:檢查發現該時段內網卡流量也下降,但沒找到原因;過了一陣子才知道,原來有工程師在線上生產環境進行效能壓力測試,佔用了大部分交換機頻寬。另一類同樣的濫用:網站資料庫有專門的 DBA 維護,如果發現資料庫存在錯誤記錄、需要進行資料訂正,必須走資料訂正流程、申請 DBA 協助;於是就有工程師為了避免麻煩,直接寫一段資料庫更新操作的程式碼,悄悄放到生產環境應用伺服器上執行,神不知鬼不覺地訂正了資料——但如果不小心寫錯了 SQL,後果可想而知。/教訓:存取線上生產環境要規範,不小心就會導致大事故。
不規範的流程引發的故障 —— 現象:某應用發布後,資料庫 Load 迅速飆升、超過報警值,回滾發布後報警解除。/原因:發現該應用發布後出現大量資料庫讀取操作,而這些資料本來應該從分散式快取讀取;檢查快取,發現資料已經被快取了;檢查程式碼,發現存取快取的那行程式碼被註解掉了。原來工程師開發的時候為了測試方便,特意註解掉讀取快取的程式碼,結果開發完成後忘記把註解去掉,直接提交到程式碼庫並被發布到線上環境。/教訓:程式碼提交前使用 diff 命令進行程式碼比較,確認沒有提交不該提交的程式碼;加強 code review,程式碼在正式提交前必須被至少一個其他工程師做過 code review,並且共同承擔因程式碼引起的故障責任。
不好的程式設計習慣引發的故障 —— 現象:某應用更新某功能後,有少量使用者投訴無法正常存取該功能,一點擊就顯示出錯訊息。/原因:分析這些使用者,都是第一次使用該功能;檢查程式碼,發現程式根據歷史使用記錄建構一個物件,如果該物件為 null,就會導致 NullPointException。/教訓:程式在處理一個輸入的物件時,如果不能明確該物件是否為空,必須做空指標判斷;程式在呼叫其他方法時,輸入的物件盡量保證不是 null,必要時建構空物件(使用空物件模式)。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 這是一份經驗清單,不是檢查表的全部。書中列的是「網站的典型故障」,價值在於提醒你:在高併發和海量資料的情況下,很多一般情況下不是問題的問題都會湧現出來。照抄九條並不能免疫,重點是養成「這件小事在高併發下會變成什麼樣」的直覺。
- 別把它讀成「要加更多防護機制」。書中 13.10 小結引一位軟體技術前輩:「軟體設計有兩種風格,一種是將軟體設計得很複雜,以使其缺陷沒那麼明顯;一種是將軟體設計得很簡單,以使其沒有明顯的缺陷。」在網站的快速發展衝擊下,即使是不明顯的缺陷也會很快凸顯出來——所以答案通常是設計得更簡單,問題能很快被發現,解決也相對容易。
- 這種知識只能靠經歷換。書中開篇說:大型網站的架構師最有價值的地方,不在於他們掌握了多少技術,而在於他們經歷過多少故障;培養一個網站架構師的成本,不單要看付了他多少薪水、給了他多少股票,還要看為他引起的故障買了多少次單。
🔗 相關工具
- 工具-無指責事後檢討 —— 配套心法,第 8 條的「共同承擔故障責任」要靠不指責的檢討文化才做得起來
- 工具-遙測與監控 —— 這九個案例幾乎都是先被監控或報警發現的,沒有遙測就只剩使用者投訴
- 工具-注入故障與韌性演練 —— 與其等第 4 條那種「關掉全部快取」意外發生,不如自己先演練一次
- 工具-防禦式編程 —— 直接對應第 9 條的空指標判斷與空物件模式
- 工具-系統化除錯 —— 每個案例的「原因分析」都是一次從現象逐層追到根因的示範
- 工具-快取與資源優化 —— 對應第 2、4、8 條,快取一旦成為架構的必要部分就不能再當作可有可無
- 工具-網站高可用與伸縮性設計 —— 上位框架,這章是它的反面教材集
- 工具-低風險發布模式 —— 第 1、2、5、8 條都是「發布後」爆掉,可回滾的發布方式能把損失壓到最小
- 工具-部署管線與持續交付 —— 對應第 5 條的啟動順序腳本與第 7 條的環境規範
- 大型網站技術架構 —— 來源書
🎯 什麼情境該想到我
當你想讓資料庫高可用、或用多副本分攤讀取,卻遇到「讀到舊資料」的怪象時。
⚙️ 怎麼用(先選架構,再處理延遲)
- 選複製架構:
- 單主(Leader-based):寫主、讀從,最常見;簡單但主是寫入瓶頸/單點。
- 多主:跨資料中心可寫,但要處理寫衝突。
- 無主(Dynamo 式):靠 quorum 讀寫。
- 同步 vs 非同步:同步保證從庫有最新值但慢/易卡;非同步快但有延遲。
- 處理複製延遲的讀取異常:
- 讀自己的寫 → 讀己所寫一致性(read-your-writes)
- 時光倒流 → 單調讀(monotonic reads)
★ 三種複製拓撲
拓撲 怎麼運作 適合 ★ 主要問題 單主(leader-based) 所有寫入走主,非同步複製給從 ★ 絕大多數場景 主掛了要選新主;複製延遲 多主(multi-leader) 多個節點都能寫 多資料中心、離線編輯 ★ 寫入衝突,而且沒有通用解法 無主(leaderless) 客戶端寫多個節點,靠 quorum Dynamo、Cassandra 一致性弱,要處理讀修復 ★ 判準:能用單主就用單主。 多主與無主帶來的衝突解決成本,遠超過它們解決的可用性問題——
除非你真的有「跨洲寫入」或「離線優先」的需求。★ 同步 vs 非同步(這是可用性與持久性的取捨)
全同步:主等所有從都確認 → ★ 一個從掛了,「整個系統不能寫」 全非同步:主不等 → ★ 主掛了,「未複製的寫入永久遺失」 ★ 半同步:至少等一個從確認 → 實務上的預設(PG 的 synchronous_standby_names)★★ 複製延遲的三個經典問題(面試必考,實務必踩)
① 讀自己的寫(read-your-writes)
使用者送出留言 → 寫入主庫 → 重新整理 → ★ 讀到從庫(還沒複製到)→ 「我的留言不見了」解法:
- 寫入後的 N 秒內,該使用者的讀都走主庫(最簡單,最常用)
- 記下寫入時的 LSN/時間戳,讀的時候要求從庫至少追到那個位置
- 「自己的資料」一律讀主庫
② 單調讀(monotonic reads)
第一次讀 → 從庫 A(新)→ 看到留言 第二次讀 → 從庫 B(舊)→ ★ 留言消失了(時光倒流)解法:★ 同一個使用者永遠路由到同一個從庫(依 user_id 雜湊)。
③ 一致前綴讀(consistent prefix reads)
A: 「你午餐吃什麼?」 B: 「便當」 ★ 觀察者可能先看到「便當」再看到問題 —— 因果順序被打亂解法:有因果關係的寫入放進同一個分區,或用邏輯時鐘追蹤因果。
★ 這三個問題的共同點:它們不是「bug」,是「非同步複製的固有後果」。
你不能靠「加更多從庫」解決,只能靠路由策略或更強的一致性保證。★ 多主的衝突解決(沒有好答案)
策略 問題 LWW(last write wins) ★ 會靜默丟失資料,而且「last」依賴時鐘(不可靠) 記下所有版本讓應用解 複雜,但至少沒有靜默遺失 CRDT ★ 數學上保證收斂,但只適用於特定資料型別(計數器、集合、文字) 避免衝突 ★ 最實用:讓同一筆資料永遠只在同一個 leader 寫(依 user 路由) ★ 「避免衝突」才是實務上的正解。衝突解決演算法是最後手段,不是設計目標。
★ 故障轉移(failover)的三個坑
坑 說明 ★ 資料遺失 非同步複製下,舊主未複製的寫入在提升新主時就沒了。舊主回來後這些寫入還要被丟棄 ★ 腦裂(split brain) 舊主沒真的死(只是網路分區),兩個主同時接受寫入 → ★ 必須有 fencing(STONITH)機制 逾時設得太短 暫時的網路抖動觸發不必要的 failover → 在高負載時引發連鎖故障 ★ 自動 failover 不是免費的。很多成熟團隊選擇半自動(自動偵測 + 人工確認),
因為錯誤的 failover 比晚幾分鐘恢復更糟。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★ 讀取副本不能解決寫入瓶頸。寫入永遠只有一個主——寫不夠就要分區(見 工具-資料分區)。
- ★ 複製延遲不是「偶爾」而是「總是」。設計時就要假設從庫落後,而不是「通常應該很快」。
→ 監控replication lag,並設告警。- ★ 「剛寫完就讀」的路徑要特別處理。這是使用者最容易察覺異常的場景(表單送出、下單、發文)。
- 「最終一致」的「最終」沒有上界。從庫卡住時可能落後數小時。要有落後過多就把它移出讀取池的機制。
- ★ 不要用 LWW 解決衝突,除非你能接受靜默的資料遺失(例如快取、遙測)。
- 跨區域複製的延遲是光速決定的(台北↔美西約 130ms RTT),不是調參數能解決的。
- 備份不等於複製。複製會把「刪除」也複製過去——誤刪的資料在所有副本上都沒了。 兩者都要做。
🔗 相關工具
- 工具-資料分區 —— 正交的另一半(複製管可用性,分區管容量)
- 工具-線性一致性與共識 —— 需要更強保證時
- 工具-故障偵測 —— failover 的前提
- 工具-交易與隔離等級 —— 單機內的一致性
- 工具-快取與資源優化 —— 快取也是一種複製,有同樣的失效問題
- 工具-反熵與Gossip傳播 —— 無主複製的修復機制
- 工具-服務容錯設計 —— failover 期間的降級
- 設計資料密集型應用
📈 伸縮性
🎯 什麼情境該想到我
當你在設計「使用者請求如何一路被導到正確、健康的服務節點」,或思考 CDN/負載均衡/網關該怎麼擺時。
⚙️ 怎麼用(一層層把流量導到對的地方,越前面擋掉越多越好)
- DNS:把網域解析到最近/可用的入口。
- CDN:靜態內容就近快取,別讓請求都打到源站。
- 負載均衡:把流量分散到多個健康節點(四層/七層)。
- API 網關:統一入口,做認證、限流、路由、聚合。
- 服務發現:讓呼叫端動態找到可用的服務實例(配合健康檢查)。
原則:能在越外層解決/擋掉的流量,就別讓它進到越內層,層層減壓。
★ 完整的分流鏈(越前面擋掉越便宜)
瀏覽器快取 ← ★ 最便宜:連請求都不發出 ↓ DNS / GSLB ← 地理就近、機房級故障轉移 ↓ CDN / 邊緣快取 ← ★ 靜態資源與可快取的 API 不進源站 ↓ WAF / DDoS 防護 ← 惡意流量在這裡被擋 ↓ L4 負載均衡 ← 四層:只看 IP:Port,極快 ↓ L7 負載均衡/API 閘道 ← ★ 認證、限流、路由、聚合 ↓ 服務(+ 服務發現、服務網格) ↓ 應用快取(Redis) ↓ 資料庫(+ 讀取副本、連線池)★ 核心原則:「每往內一層,成本大約高一個數量級。」
一個能在 CDN 命中的請求,成本大約是打到資料庫的千分之一。
所以架構優化的第一個問題永遠是:「這個請求能不能在更外層就被解決?」★ L4 vs L7 負載均衡
L4(傳輸層) L7(應用層) 看得到什麼 IP、Port、TCP 連線 ★ HTTP 方法、路徑、header、cookie 能做什麼 依連線分流 ★ 依路徑路由、依使用者黏著、重寫、壓縮、TLS 終止 效能 ★ 極高(可以只改封包不拆解) 較低(要解析 HTTP) 健康檢查 TCP 連得上就算活 ★ 能打 /healthz看真實狀態例子 LVS、AWS NLB Nginx、Envoy、AWS ALB ★ 實務上常兩層都有:L4 做第一層分散與 DDoS 吸收,L7 做智慧路由。
★ API 閘道:該放什麼、不該放什麼
✅ 該放(橫切關注點) ❌ 不該放 認證與 token 驗證 ★ 業務邏輯 限流與配額(見 工具-限流器設計) ★ 資料轉換與聚合的複雜邏輯 路由與版本 需要頻繁改動的東西 請求/回應日誌與 trace 注入 任何「屬於某個團隊」的規則 TLS 終止、壓縮、CORS ★★ API 閘道最大的風險:它會變成新的單體。
每個團隊都想在閘道加一點邏輯 → 它變成所有人都要改、誰都不敢動的中心元件,
而且它掛了全站都掛。
→ 閘道只放「所有服務都一樣」的東西;只要有「某個服務特別」的邏輯,就該放回服務裡。★ 快取的層次與失效難度
層 命中的價值 ★ 失效的難度 瀏覽器 最高(零請求) ★ 最難(你無法主動清除別人的瀏覽器) CDN 很高 中(可以 purge,但有延遲) 閘道/反向代理 高 中 應用內(Redis) 中 ★ 最容易(你完全控制) ★ 這張表解釋了一個重要的取捨:
越外層越省,但也越難改。
→ 內容會變的東西用短 TTL 或放內層;不會變的(帶 hash 的靜態資源)才用長 TTL 放最外層。
→ 靜態資源用 content hash 檔名(app.a3f1c2.js)+ 永久快取,是這個取捨的標準解法。★ 服務發現
方式 例子 DNS ★ 簡單,但有快取與 TTL 問題 註冊中心 Consul、etcd、Eureka ★ 平台內建 Kubernetes Service(多數情況的正解) 服務網格 Istio / Linkerd —— ★ 把重試、逾時、mTLS、追蹤下沉到 sidecar ★ 服務網格的取捨:它把容錯邏輯從應用抽出來(好),但多一層代理的延遲、更多的運維複雜度、更難除錯(壞)。
服務數量少於一二十個時,通常不值得。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★★ 每一層都是一個可能的單點。加一層 = 多一個會掛的東西、多一跳延遲、多一個要監控的對象。
不要為了「架構完整」而把每一層都加上。- ★ 快取失效是最難的部分。加快取容易,「什麼時候該讓它失效」才是問題。
沒想清楚就加 → 「資料明明改了但使用者看到舊的」(見 工具-快取與資源優化)。- ★ 閘道不要變成單體(見上)。
- DNS 的 TTL 不可靠。很多解析器會忽略你設的 TTL → 不要依賴 DNS 做快速故障轉移。
- TLS 終止的位置決定了內網是否加密。合規要求端到端加密時,不能只在邊緣終止。
- 健康檢查要檢查「真的能做事」,不是只回 200(見 工具-故障偵測)。
- 小系統不需要這麼多層。一個 Nginx + 應用 + 資料庫能撐很久。先量再加。
🔗 相關工具
- 工具-快取與資源優化 —— 快取策略與失效
- 工具-限流器設計 —— 閘道層的保護
- 工具-服務容錯設計 —— 每一層都該有的容錯
- 工具-故障偵測 —— 健康檢查的設計
- 工具-網站高可用與伸縮性設計 —— 更上層的可用性設計
- 工具-前端效能優化規則 —— 瀏覽器那一端
- 工具-網站架構模式九式 —— 架構手法的完整目錄
- 鳳凰架構
🎯 什麼情境該想到我
當單一機器裝不下資料/扛不住流量,需要把資料切分到多台機器(水平擴展)時。
⚙️ 怎麼用
- 選分區鍵與策略:
- 按範圍分區:範圍查詢方便,但易產生熱點(如全部照時間寫入同一分區)。
- 按雜湊分區:分佈均勻、避熱點,但失去範圍查詢能力。
- 處理熱點:熱鍵可加隨機前綴打散。
- 次級索引:本地索引(分區內,查詢要 scatter/gather)vs 全域索引(寫入要跨分區)。
- 再平衡(rebalancing):用固定數量分區等策略,避免資料大搬風。
★ 分區鍵的選擇(這一步決定成敗,而且極難反悔)
策略 做法 優點 ★ 致命弱點 按範圍 依 key 的區間切( a–f、g–m)★ 範圍查詢快(同一分區內) ★ 熱點:時間序列全部寫進最新的那一片 按雜湊 hash(key) % n決定分區★ 分佈均勻 ★ 失去範圍查詢; % n讓加節點要搬幾乎所有資料★ 一致性雜湊 雜湊到環上,節點也在環上 ★ 加減節點只搬 1/n 的資料 實作較複雜(見 工具-一致性雜湊) 複合鍵 (tenant_id, timestamp)★ 兼顧兩者:租戶內可範圍查詢,跨租戶均勻 要選對主序 ★ 實務上最常見的正確答案是「複合鍵」:
高基數的欄位當分區鍵(決定去哪一片),低基數/有序的當排序鍵(決定片內的順序)。
Cassandra 的PRIMARY KEY ((tenant_id), created_at)就是這個形狀。★ 熱點的三種成因與對策
成因 例子 對策 時間遞增的 key 用 created_at或自增 ID 當分區鍵★ 改用雜湊或複合鍵;或反轉時間戳的位元 ★ 名人問題(celebrity) 某個大客戶佔 40% 流量 ★ 加隨機前綴打散: key→{rand(0..9)}#{key},讀的時候要查 10 片再合併存取偏斜 少數熱門商品 快取擋在前面(見 工具-快取與資源優化)——比分區便宜得多 ★ 加隨機前綴是有代價的:寫入分散了,但每次讀都要 scatter-gather。
只對確定的熱鍵做,不要全表都加。★ 次級索引:兩種都有嚴重缺點
① 本地索引(document-partitioned) 每個分區維護「自己那份資料」的索引 ✅ 寫入只碰一個分區(快) ❌ ★ 查詢要問「所有」分區再合併(scatter-gather) → 延遲 = 最慢的那一片;分區越多尾延遲越糟 ② 全域索引(term-partitioned) 索引本身也被分區(依索引的 term 分) ✅ 查詢只問一個分區(快) ❌ ★ 寫入一筆資料要更新「多個」分區的索引 → 需要分散式交易,否則索引會與資料不一致(多半是非同步更新 → 最終一致)★ 這是分區系統無法迴避的取捨:寫快讀慢,或讀快寫慢。
多數系統選 ①(本地索引 + scatter-gather),因為寫入路徑的簡單性更重要。★ 再平衡:為什麼「固定數量的分區」是主流
❌ hash(key) % n n 從 3 變 4 → ★ 幾乎所有 key 的歸屬都變了 → 全量資料搬遷 ✅ 固定數量的邏輯分區(例如一開始就切 1024 片) 節點數變化時,只是「哪些分區歸哪個節點」改變 ★ 搬的是「整片」,數量可控,而且分區鍵的計算永遠不變★ 這是 Kafka(partition 數)、Elasticsearch(shard 數)、Riak 的做法。
代價:分區數一開始就要定好,之後很難改(Kafka 可以加但不能減,而且加了會破壞 key 的順序保證)。動態分區(HBase、MongoDB)則是「太大就分裂、太小就合併」——彈性好,但再平衡的時機不可控。
★ 請求路由的三種做法
做法 例子 客戶端自己算(胖客戶端) Cassandra 的 driver 中間的路由層 ★ ProxySQL、Vitess、mongos 任一節點都能轉發 Cassandra、Riak(gossip 同步拓撲,見 工具-反熵與Gossip傳播) 🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★★ 先別分區。 單機 PostgreSQL 撐到幾 TB、幾萬 QPS 是常態。
分區帶來的複雜度(跨分區 JOIN、分散式交易、再平衡、運維)遠超過多數團隊的預期。
先做:加索引、加快取、加讀取副本、垂直擴展、歸檔舊資料。- ★ 分區鍵幾乎不可能改。改分區鍵 = 全量資料遷移 + 應用改寫。這是你會做的最難反悔的決定之一。
- ★ 跨分區的操作全部變貴:JOIN、交易、
ORDER BY+LIMIT、COUNT(*)、唯一性約束。
→ schema 設計要讓「一起查的資料落在同一分區」(這是 工具-限界上下文 與聚合設計的實際價值)。- 分區 ≠ 複製。分區解決「容量」,複製解決「可用性與讀取吞吐」——兩者正交,通常要一起做(見 工具-資料複製)。
- ★ 唯一性約束跨分區時會失效。全域唯一的 email 需要額外的機制(獨立的唯一性表、或把 email 當分區鍵)。
- 監控要 per-partition。整體平均看起來很好,但某一片已經爆了——熱點只有分片層級的指標看得出來。
- 再平衡不要全自動。自動再平衡在節點暫時不可用時可能觸發連鎖搬遷 → 雪崩。要有人工確認的閘門。
🔗 相關工具
- 工具-一致性雜湊 —— 再平衡時搬最少資料
- 工具-資料複製 —— 正交的另一半
- 工具-快取與資源優化 —— ★ 比分區便宜得多的第一選擇
- 工具-限界上下文 —— 讓「一起查的資料」落在同一分區
- 雜湊表 —— 分區與雜湊的共同基礎
- 工具-容量估算 —— 決定要不要分區的依據
- 工具-反熵與Gossip傳播 —— 節點怎麼知道拓撲
- 設計資料密集型應用
🎯 什麼情境該想到我
當你「要辦一場開賣瞬間就湧入數百倍平時流量的限量搶購活動,又不想為了這幾秒鐘去養一整年用不到的伺服器、更不想把正常業務一起拖垮」的時候。
⚙️ 怎麼用(步驟 / 公式)
第一步:先把四個技術挑戰列出來(以「一件商品、預計 1 萬人參加、最大並發請求數 10,000」為基準)
- 對現有網站業務造成衝擊——秒殺是營銷附加活動,時間短、並發訪問量大,如果和原有應用部署在一起,稍有不慎可能導致整個網站癱瘓。
- 高並發下的應用、資料庫負載——使用者在秒殺開始前會不停刷新瀏覽器頁面以確保不錯過,這些請求若走一般架構去訪問應用伺服器、連資料庫,會對應用伺服器和資料庫伺服器造成極大負載壓力。
- 突然增加的網路及伺服器頻寬——假設商品頁面大小 200K(主要是商品圖片),需要的網路和伺服器頻寬就是 200K × 10,000 = 2G,這是活動新增的、超過平時使用的頻寬。
- 直接下單——遊戲規則是到了秒殺時間才能下單,但下單頁面也只是一個普通的 URL,拿到這個 URL 就可以不等活動開始直接下單。
第二步:套四項應對策略(一項對一個挑戰)
- 秒殺系統獨立部署——不與正常網站交易業務共用伺服器。如果需要,還可以使用獨立的域名,使其與網站完全隔離;即使秒殺系統崩潰了,也不會對網站造成任何影響。
- 秒殺商品頁面靜態化——重新設計頁面,不用原本的商品詳情頁,把商品描述、商品參數、成交記錄、使用者評價全部寫入一個靜態頁面。使用者請求不需經過應用伺服器的業務邏輯處理,也不需訪問資料庫,所以秒殺商品服務不需要部署動態的 Web 伺服器和資料庫伺服器。
- 租借秒殺活動網路頻寬——新增的頻寬必須和運營商重新購買或租借;並把秒殺商品頁面快取在 CDN,同樣要和 CDN 服務商臨時租借新增的出口頻寬。
- 動態生成隨機下單頁面 URL——在下單頁面 URL 加入由伺服器端生成的隨機數作為參數,秒殺開始的時候才能得到,讓即使是秒殺系統的開發者也無法在開始前訪問下單頁面 URL。
第三步:頁面與表單一律砍到最簡
- 商品頁面盡可能簡單(只有商品圖片、商品資訊、購買按鈕、商品描述)——參與者關心的是「怎麼快速刷新頁面、開始時搶先進入下單頁」,而不是商品詳情等體驗細節。購買按鈕只有在活動開始時才變亮,在此之前及商品賣出後都是灰色不可點擊。
- 下單表單盡可能簡單:購買數量只能是一個且不可修改;送貨地址與付款方式都用使用者預設設定,沒有預設也可以不填,允許等訂單提交後再修改;只有第一個提交的訂單發送給網站的訂單子系統,其餘使用者提交後只能看到秒殺結束頁面。
第四步:解掉兩個額外問題
- 問題一:怎麼控制購買按鈕的點亮?
頁面已被設計成靜態頁、快取在 CDN/反向代理伺服器甚至使用者瀏覽器上,秒殺開始時使用者刷新頁面,請求根本不會到達應用伺服器,所以不能靠伺服器端構造回應頁面來控制。
解法是用 JavaScript 腳本控制:在秒殺商品靜態頁面中加入一個 JavaScript 檔案引用,該檔案中放入「秒殺是否開始的標誌」與「下單頁面 URL 的隨機數參數」;秒殺開始的時候生成一個新的 JavaScript 檔案推送到 JavaScript 伺服器並被使用者瀏覽器載入,控制商品頁面的展示。
關鍵條件:這個 JavaScript 檔案使用隨機版本號,並且不被瀏覽器、CDN 和反向代理伺服器快取;因為檔案非常小,即使每次刷新都去訪問它,也不會對伺服器集群和網路頻寬造成太大壓力。- 問題二:怎麼只讓第一個提交的訂單進到訂單子系統?
為了減輕下單頁面伺服器的負載壓力,控制進入下單頁面的入口,只有少數使用者能進入下單頁面,其他人直接進入秒殺結束頁面。
具體門檻:假設下單伺服器集群有 10 台伺服器,每台伺服器只接受最多 10 個下單請求。下單流程為——活動開始 → 點擊購買按鈕 → 請求發送至下單伺服器 → 下單伺服器檢查本機處理下單請求數目(已超過 10 → 顯示秒殺活動已結束頁面;未超過 10 → 放行)→ 使用者填寫訂單、點擊提交 → 檢查全域已提交訂單數目(已超過秒殺商品總數 → 顯示秒殺活動已結束頁面;未超過 → 提交到訂單子系統)。第五步:照這份清單佈署(本書給出的整體架構)
- 網站機房秒殺伺服器集群:
- 秒殺商品伺服器集群:10 台 Apache 伺服器
- JavaScript 伺服器集群:5 台 Lighttpd 伺服器
- 定時任務伺服器:1 台 Linux 伺服器(秒殺開始前生成新的 JavaScript 檔案並推送到 JavaScript 伺服器)
- 全域計數器伺服器:1 台 Memcached 伺服器
- 下單伺服器集群:10 台 Jetty 伺服器
- 外圍:使用者瀏覽器 → CDN 伺服器(承接刷新秒殺商品頁面,每次刷新都會載入一次 JavaScript 檔案);下單伺服器把通過檢查的訂單提交到網站機房業務伺服器集群的訂單處理子系統。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 不要追求完美的公平。秒殺是對網站架構的極大考驗,在難以預計和控制的高並發訪問衝擊下稍有不慎,系統就會「被使用者秒殺」導致整個系統宕機、活動失敗、構成重大事故。因此在遵循秒殺活動遊戲規則的基礎上,為了保證系統的安全,保持適度的公平公正即可——上面「每台伺服器只收 10 個請求」這種做法本來就會篩掉絕大多數使用者,這是刻意的取捨。
- 故障也不准顯示錯誤頁。書中明講:即使系統出了故障,也不應該給使用者顯示出錯頁面,而是顯示秒殺活動結束頁面,避免不必要的困擾。
- 這套架構是為秒殺而設計的專用系統,不同於一般網購行為,不能拿正常的網站業務流程來套,也不能和正常的網站交易業務共用伺服器。
- 除了本卡列的架構設計,書中第 10 章提到的許多效能最佳化設計都可以用於秒殺系統的最佳化。
🔗 相關工具
- 工具-容量估算 —— 本卡的起點就是一次容量估算:1 萬人 → 10,000 並發 → 200K × 10,000 = 2G 頻寬,數字算出來才知道要租多少頻寬、佈幾台機器
- 工具-快取與資源優化 —— 頁面靜態化 + CDN 快取是秒殺的主力手段,而「JavaScript 檔案帶隨機版本號且不被快取」正是快取失效控制的典型應用
- 工具-透明多級分流 —— 秒殺把絕大多數請求擋在瀏覽器、CDN、反向代理這幾層,根本不讓它們到達應用伺服器與資料庫
- 工具-限流器設計 —— 「每台下單伺服器只接受最多 10 個請求」+「全域計數器檢查商品總數」就是一組本機加全域的兩層限流
- 工具-網站高可用與伸縮性設計 —— 秒殺系統獨立部署、獨立域名、與網站完全隔離,讓秒殺系統即使崩潰也不影響網站
- 大型網站技術架構 —— 來源書
🧩 擴展性
🎯 什麼情境該想到我
當你在猶豫「現在就該上分散式/微服務/複雜中間件嗎,還是先簡單做」時。
⚙️ 怎麼用
- 在痛點真的出現時才演進:DB 撐不住才分庫分表、單體真的擋路才拆服務。別為「想像中的未來規模」預先過度設計。
- 每次升級對準一個真實瓶頸:先量測、確認瓶頸,再針對它升級(呼應 工具-找出並管理約束點)。
- 保留演進空間,但不預先實作:介面/邊界留好(工具-限界上下文),讓未來好改,但現在保持簡單。
- 把共通難題沉澱成平台/中間件:等同類需求重複出現,再抽象共用,而非一開始就造框架。
📌 這一組三張卡:工具-服務架構的演進與拆分=「有哪些選項」|本卡=「什麼時候該動」|工具-服務邊界與演進式拆分=「切在哪」
★ 判準:等「觸發訊號」出現,不要等「預感」
演進動作 ★ 真正的觸發訊號 ❌ 不是訊號 加讀取副本 讀取造成主庫 CPU/IO 飽和 「聽說要讀寫分離」 加快取 同樣的查詢重複率高 + DB 是瓶頸 「先加快取比較快」 分庫分表 ★ 單表已數億列且索引失效、或單機容量/寫入到頂 「資料會長很大」 拆服務 ★ 團隊互相擋住部署、資源需求差異極大 「模組太多了」 上訊息佇列 需要削峰 / 解耦 / 非同步化 「架構圖有比較好看」 多地區部署 有跨地區的使用者且延遲是問題、或有合規要求 「以後要國際化」 ★ 共同點:每一個「真正的訊號」都是可量測的。
說不出「現在的數字是多少、到多少才需要動」,就是還不該動。★ 演進的正確節奏
① ★ 量測 → 找出「現在」真正的瓶頸(不是猜的) ② 針對「那一個」瓶頸做最小的改動 ③ 再量測 → 確認瓶頸移動了 ④ 回到 ①★ 一次只解一個瓶頸。 同時改三件事 → 你不會知道哪一件有效(見 工具-用度量取代模糊判斷)。
Amdahl 的提醒:優化只佔 5% 時間的部分,最多只能快 5%。
→ 先量出「時間花在哪」,再決定優化什麼(見 工具-找出並管理約束點)。★ 預留空間 vs 預先實作(這條線最難拿捏)
✅ 該做(便宜、可逆) ❌ 不該做(昂貴、難逆) ★ 模組邊界劃清楚(見 工具-限界上下文) 預先拆成微服務 ★ 介面收窄(不暴露內部結構) 預先做一層抽象以支援「未來可能的第二種實作」 ★ ID 用可排序且不衝突的(UUIDv7/Snowflake,未來好分片) 預先分庫分表 ★ schema 保留擴充欄位、用可空欄位 預先設計「通用的 EAV 表」 ★ 把狀態集中(讓服務無狀態) 預先引入服務網格 ★ 遙測從第一天就有 預先建平台團隊 ★ 判準:這件事「現在做」和「以後做」的成本差多少?
差很多(例如 ID 格式、資料模型、邊界)→ 現在做。
差不多(例如加快取、加副本、拆服務)→ 以後做。這也是為什麼「資料模型」值得多想,而「部署架構」不值得預先設計——
資料模型改起來痛,部署架構改起來相對容易。★ 各階段的典型形態(參考,不是規範)
規模 典型架構 什麼會先爆 < 1 萬 DAU ★ 單體 + 一個資料庫 + 託管服務 通常什麼都不會爆。別過度設計 1–10 萬 + CDN + 快取 + 讀取副本 資料庫讀取 10–100 萬 + 模組化單體或少數服務 + 佇列 + 物件儲存 寫入、團隊協作 100 萬+ 服務化 + 分區 + 多地區 一致性、運維複雜度 ★ 多數系統永遠停在第一、二階段,而那是完全正常的。
「為了一百萬使用者設計」而實際只有一千個,是最常見也最昂貴的錯誤。★ 反過來也要會:簡化
演進不只是「加東西」。 用不到的服務要合併回去、失效的快取層要拆掉、
過時的抽象要移除(見 工具-技術債功能凍結)。
★ 「架構只會變複雜」是一個可以打破的假設,但需要刻意安排時間。🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意 / 什麼時候不適用
- ★ 「痛點出現才演進」不等於「等到爆了才動」。
要在「趨勢明確」時就開始——磁碟用量每月漲 10%、p99 每週惡化 5%,
這些是訊號,不要等到 100% 才反應。 關鍵是有量測、有趨勢,不是有預感。- ★ 有些東西無法演進,必須一開始就對:資料模型、ID 格式、租戶隔離方式、稽核紀錄。
這些改起來要資料遷移 + 應用重寫——值得多花時間想。- 不要因為「面試都這樣答」而做。你的系統不是 Twitter。
- 演進要有回滾路徑。加了快取結果一致性出問題 → 要能關掉(feature flag)。
- ★ 團隊能力也是約束。三個人維運不了 Kubernetes + 服務網格 + 多地區部署——
選擇你「養得起」的架構(見 合理地使用技術)。- 量測本身要先有。沒有遙測就沒有觸發訊號,只能憑感覺(見 工具-遙測與監控)。
🔗 相關工具
- 工具-服務架構的演進與拆分 —— 有哪些選項與各自的代價
- 工具-服務邊界與演進式拆分 —— 決定要拆之後,切在哪
- 工具-找出並管理約束點 —— ★ 一次只解一個瓶頸
- 工具-用度量取代模糊判斷 —— 觸發訊號要可量測
- 工具-遙測與監控 —— 量測的基礎
- 工具-容量估算 —— 判斷「還有多久會到頂」
- 工具-技術債功能凍結 —— 反過來的簡化
- 合理地使用技術 —— 選養得起的架構
🔒 安全性
🎯 什麼情境該想到我
當你「開放使用者輸入或上傳任何東西給網站」的時候。
⚙️ 怎麼用(三大攻擊 × 防禦手段)
書中開宗明義:全球大約 70% 的 Web 應用攻擊都來自 XSS 攻擊和 SQL 注入攻擊,此外常用的手段還包括 CSRF、Session 劫持等。
1. XSS 攻擊(跨站點腳本 Cross Site Script)
黑客竄改網頁、注入惡意 HTML 腳本,在使用者瀏覽網頁時控制其瀏覽器做惡意操作。可用來偷 Cookie、密碼,進而偽造交易、盜竊財產、竊取情報。
兩種型態:
- 反射型:誘使使用者點一個嵌了惡意腳本的連結。新浪微博那次就是反射型——微博裡有一個含惡意腳本 URL,點了會自動關注攻擊者的 ID 並再發一篇含同樣 URL 的微博,攻擊就擴散了。
- 持久型:黑客提交含惡意腳本的請求,存進被攻擊站點的資料庫,其他使用者瀏覽時腳本被包在正常頁面裡執行。常見於論壇、部落格這類應用。
兩種防禦:
- 消毒:對某些 HTML 危險字元轉義(
>轉為>、<轉為<)。關鍵細節:要文字比對後再轉義,避免把「3<5」裡的<誤轉;只有像<img src=這種上下文中的<才轉。消毒幾乎是所有網站最必備的 XSS 防護手段。- HttpOnly:最早由微軟提出,瀏覽器禁止頁面 JavaScript 存取帶 HttpOnly 屬性的 Cookie。注意它不是直接對抗 XSS,而是防止 XSS 攻擊者竊取 Cookie;存放使用者認證等敏感資訊的 Cookie 應加上此屬性。
2. 注入攻擊(SQL 注入 / OS 注入)
攻擊者在 HTTP 請求中注入惡意 SQL(例如
drop table users;),伺服器用請求參數拼出 SQL 命令時,惡意 SQL 被一起拼進去並在資料庫執行。攻擊者取得資料庫表結構的三種途徑:
- 開源——網站用開源軟體搭建(如用 Discuz! 搭論壇),資料庫結構本來就公開。
- 錯誤回顯——網站開著錯誤回顯,伺服器內部 500 錯誤直接顯示到瀏覽器;攻擊者故意構造非法參數,讓例外訊息輸出到瀏覽器端,方便猜表結構。
- 盲注——網站關掉錯誤回顯,攻擊者靠頁面變化判斷 SQL 執行情況據以猜測,難度較大。
防禦: 首先要避免被猜到表名等結構資訊,此外
- 消毒:透過正則比對,過濾請求資料中可能注入的 SQL(如
drop table)。簡單粗暴但有效。- 參數綁定:使用預編譯手段,綁定參數是最好的防 SQL 注入方法。iBatis、Hibernate 等資料存取層框架都實作了 SQL 預編譯與參數綁定,攻擊者的惡意 SQL 會被當成 SQL 的參數而非命令執行。
除了 SQL,攻擊者也會視應用注入 OS 命令、程式語言程式碼。
3. CSRF 攻擊(跨站點請求偽造 Cross Site Request Forgery)
攻擊者透過跨站請求,在使用者不知情下以其合法身分做非法操作(轉帳交易、發表評論等)。核心是利用了瀏覽器 Cookie 或伺服器 Session 策略來盜用身分。防禦主軸是「識別請求者身分」:
- 表單 Token:在頁面表單中加一個隨機數當 Token,每次回應頁面的 Token 都不同;正常頁面提交的請求會帶上,偽造的請求拿不到這個值,伺服器檢查 Token 存在且正確才放行。
- 驗證碼:更簡單有效,但書中明講輸入驗證碼是一個糟糕的使用者體驗,所以請在必要時使用,如支付交易等關鍵頁面。
- Referer check:HTTP 請求頭的 Referer 域記錄請求來源,檢查來源是否合法。很多網站用這個做圖片防盜鏈。
4. 其他攻擊和漏洞(8.1.4)
- Error Code(錯誤回顯):許多 Web 伺服器預設打開例外訊息輸出,未處理的例外堆疊直接吐到瀏覽器,黑客可故意製造非法輸入來找漏洞。防禦很簡單——設定 Web 伺服器參數,把 500 頁面跳轉到專門的錯誤頁;常用的 MVC 框架也有這功能。
- HTML 註釋:開發人員為了除錯,在 PHP、JSP 等伺服器頁面程式中用 HTML 註釋語法寫註解,這些註釋會顯示在客戶端瀏覽器。發布前要做 code review 或自動掃描。
- 檔案上傳:若上傳的是可執行程式並藉此取得伺服器端命令執行能力,攻擊者幾乎能為所欲為,還能當跳板打叢集內其他機器。最有效的防禦是設上傳檔案白名單,只允許可靠的檔案類型;此外可修改檔名、使用專門的儲存。
- 路徑遍歷:攻擊者在 URL 中用相對路徑,遍歷系統未開放的目錄與檔案。防禦是把 JS、CSS 等資源檔部署在獨立伺服器、用獨立網域,其他檔案不用靜態 URL 存取,動態參數不包含檔案路徑資訊。
5. Web 應用防火牆:ModSecurity(8.1.5)
理想的產品要能統一攔截請求、過濾惡意參數、自動消毒、加 Token,並根據最新攻擊與漏洞情報不斷升級對策。ModSecurity 就是這樣的東西:
- 開源的 Web 應用防火牆,可嵌入 Web 應用伺服器,也可當獨立應用程式啟動。最早只是 Apache 的一個模組,書中寫作當時已有 Java、.NET 多個版本並支援 Nginx。現況(查證 2026-10-15):專案已由 Trustwave SpiderLabs 轉到 OWASP 名下(
github.com/owasp-modsecurity/ModSecurity,舊 SpiderLabs 網址轉址過去),v3 改為與伺服器無關的函式庫 libmodsecurity,再透過各自獨立的 connector(如 ModSecurity-nginx)接 Nginx/Apache/IIS,仍持續發版(最新 v3.0.17,2026-09-29)。- 架構重點:處理邏輯與攻擊規則集合分離。處理邏輯(執行引擎)負責請求/回應的攔截過濾、規則載入執行;攻擊規則集合負責描述具體攻擊的規則定義、模式識別、防禦策略(可用文字方式描述)。處理邏輯比較穩定,規則集合則需針對漏洞不斷升級——這是一種可擴展的架構設計。
- 商業產品也有實作此功能者,如 NEC 的 SiteShell。
6. 網站安全漏洞掃描(8.1.6)
和電腦安全漏洞掃描一樣,網站也需要。掃描工具根據內建規則構造具有攻擊性的 URL 請求,模擬黑客攻擊行為,用以發現漏洞。許多大型網站的安全團隊都有自己開發的掃描工具,不定期掃描伺服器查漏補缺;市場上也有商用的掃描平台。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 沒有「百毒不侵、固若金湯」的網站。書中反問「有沒有百毒不侵、固若金湯的網站呢?」,而 XSS 這種「古老」手段歷久彌新、不斷變化出新花樣,許多以前認為不可能用來攻擊的漏洞也逐漸被利用——防禦是持續升級的過程,不是一次性的驗收項目。
- 這也是 ModSecurity 把規則集合獨立出來的理由:新型漏洞不斷被報告,只有規則能持續升級的架構才跟得上。
- 驗證碼雖有效但傷體驗,別無差別地套在所有表單上。
🔗 相關工具
- 工具-資安與合規左移 —— 把這些攻擊面在設計與開發階段就處理掉,而不是等上線後靠掃描補
- 工具-防禦式編程 —— 消毒、白名單、參數綁定的上位心法:不信任任何外部輸入
- 工具-用例外處理錯誤 —— 對應 Error Code 漏洞,例外不該原封不動吐給使用者
- 工具-透明多級分流 —— WAF 就掛在流量入口那一層,和負載均衡同屬請求進來的第一道
- 工具-網站高可用與伸縮性設計 —— 同書的另一面,安全與可用性都是架構的非功能需求
- 工具-LLM安全防護 —— 同樣的注入思維在 LLM 時代的變形
- 大型網站技術架構 —— 來源書
🎯 什麼情境該想到我
當你「要保護敏感資料,得決定用哪一類加密、以及那把金鑰到底該放哪裡」的時候。
⚙️ 怎麼用(步驟 / 公式)
先問「明文有沒有偷偷躺在某處」。2011 年 12 月被曝的 CSDN 密碼洩漏事故,網站安全措施不力導致使用者資料庫被駭客「拖庫」並不稀奇,令人錯愕的是資料庫中的使用者密碼竟然是明文保存,導致密碼洩漏、成為地下黑市交易的商品。動手加密前,先把所有明文落地點盤一遍。
選加密類型。書中把資訊加密技術分成三類:
- 單向散列加密:對不同輸入長度的資訊做散列計算,得到固定長度的輸出,過程是單向的——不能從固定長度的輸出反算回輸入。
- 對稱加密:加密和解密使用的是同一把金鑰(或可互相推算)。
- 非對稱加密:加解密用的不是同一把金鑰,一把對外公開叫公鑰,另一把只有所有者知道叫私鑰;公鑰加密的只有私鑰能解,反之亦然。理論上不可能由公鑰計算得到私鑰。
密碼保存用單向散列。使用者註冊時輸入的密碼不直接存進資料庫,而是先做單向散列加密、把密文存入資料庫;登入時同樣算出輸入密碼的密文,與資料庫中的密文比較,一致才驗證成功。這樣即使資料庫被「拖庫」也不會洩漏密碼資訊。
- 一定要加鹽(salt):雖然不能用演算法把散列密文反算成明文,但人們設定密碼有一定模式,透過彩虹表(常用密碼與對應密文的關係表)等手段可以做猜測式破解。加 salt 相當於加密的金鑰,增加破解難度。
- 常用單向散列演算法:MD5、SHA。
- 附帶特性:輸入的任何微小變化都會導致輸出完全不同——因此也用來產生資訊摘要、計算高離散程度的隨機數。
大量資料加密用對稱加密。常用在資訊需要安全交換或儲存的場合,如 Cookie 加密、通訊加密。
- 優點:演算法簡單、加解密效率高、系統開銷小,適合對大量資料加密。
- 缺點:加解密同一把金鑰,遠端通訊時如何安全交換金鑰是個難題;金鑰一旦遺失,所有加密資訊也就無密可言。
- 常用演算法:DES、RC。書中稱對稱加密是傳統也是最常用的加密手段,適用於絕大多數需要加密的場合。
安全傳輸與數位簽章用非對稱加密,注意兩者方向相反:
- 安全傳輸:發送者 A 從公開渠道取得接收者 B 的公鑰加密,經非安全通道送出;B 用自己的私鑰解密。密文被竊取者拿到也還原不出明文。
- 數位簽章:簽名者用自己的私鑰加密後發出,接收方用簽名者的公鑰解密。由於私鑰只有簽名者擁有,該資訊不可抵賴,具有簽名性質。
- 常用演算法:RSA。HTTPS 傳輸中瀏覽器使用的數位憑證,實質上就是經權威機構認證的非對稱加密公鑰。
實務上混合使用:先用非對稱加密技術對對稱金鑰進行安全傳輸,再用對稱加密技術做資訊加解密與交換。有時對同一份資料兩次使用非對稱加密,可同時達成安全傳輸與數位簽章。
回頭管好金鑰——這才是前提。不管是散列的 salt、對稱加密的金鑰還是非對稱加密的私鑰,一旦洩漏,所有基於它加密的資訊都失去秘密性。書中直接點名的壞習慣:有的工程師把金鑰直接寫在原始碼中,稍好一點的寫在設定檔中,線上與開發環境用不同金鑰——但金鑰本身仍以明文保存,而且很多人接觸得到,至少在公司內部金鑰不是秘密。
兩種改善方案,二選一:
- (a) 金鑰與演算法都獨立部署:放在一台獨立伺服器上,甚至做成專用硬體設施,對外提供加解密服務,應用系統呼叫這個服務完成加解密。由專人維護,金鑰洩漏機率大大降低。缺點:成本較高、有可能成為應用的瓶頸、每次加解密都要一次遠端服務呼叫,系統效能開銷也較大。
- (b) 演算法留在應用系統、金鑰放獨立伺服器:兼顧安全性與效能。實際儲存時,金鑰被切分成數片,加密後分別保存在不同儲存介質中。應用程式呼叫加解密服務介面,介面向密鑰伺服器的密鑰服務取得金鑰並在本地快取(定時更新);密鑰伺服器的金鑰則來自多台密鑰儲存伺服器,每台都有專人負責管理。密鑰申請者、密鑰管理者、安全稽核人員透過密鑰管理控制台管理更新金鑰,每個人各司其職,沒有人能查看完整的密鑰資訊。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 單向散列不可逆,只能用來驗證、不能用來取回。要能還原原始資料的場合(Cookie 內容、通訊內容)得用對稱或非對稱加密。
- 只加密不管金鑰等於沒加密。書中明說,前述幾種加密技術能達到保密效果的重要前提就是金鑰的安全。
- 獨立加解密服務會變成瓶頸:每次加解密都是一次遠端呼叫,高併發路徑上要先評估開銷;書中方案 (b) 就是為此而生。
- 非對稱加密不適合拿來加密大量資料:書中的定位是資訊安全傳輸與數位簽章,大量資料交給對稱加密。
🔗 相關工具
- 工具-網站應用攻擊與防禦 —— 同一章的另一半:擋住 XSS/注入等攻擊,加密則是攻擊得手後的最後一道防線
- 工具-反垃圾與電商風控 —— 同一章的內容安全與交易安全篇
- 工具-資安與合規左移 —— 把「金鑰不能寫在程式碼裡」這類要求提前到開發流程前段
- 工具-金鑰地址與錢包 —— 公私鑰的另一個應用場景:私鑰即所有權
- 工具-防禦式編程 —— 不信任外部輸入的同一種心態,往下延伸到不信任儲存層
- 大型網站技術架構 —— 來源書
🎯 什麼情境該想到我
當你「要用機器擋掉垃圾內容或高風險交易,而人工審核已經審不動」的時候。
⚙️ 怎麼用(步驟 / 公式)
A. 文字比對(擋敏感詞)
- 網站維護一份敏感詞列表,若使用者發表的資訊含有列表中的敏感詞,就做消毒處理(把敏感詞轉義為 ***)或拒絕發表。
- 選比對方法:
- 敏感詞比較少、使用者提交的文字長度也短 → 直接用正則表達式比對。
- 但正則的效率一般較差;當敏感詞很多、發布資訊也很長、網站並發量較高時要換方法。公開演算法基本上都是 Trie 樹的變種,空間與時間複雜度都比較好的有雙陣列 Trie 演算法。
- Trie 演算法的本質是確定一個有限狀態自動機,根據輸入資料進行狀態轉移。雙陣列 Trie 用兩個稀疏陣列儲存樹結構:base 陣列存 Trie 樹的節點,check 陣列進行狀態檢查。需要依業務場景與經驗決定陣列大小,避免陣列過大或衝突過多。
- 更簡單的實作是多級 Hash 表過濾樹:例如敏感詞表含「阿拉伯、阿拉汗、阿油、北京、北大荒、北風」,就構造一棵過濾樹,使用者提交的資訊逐字依序在樹中比對;過濾樹分支可能較多,把同一層中相同父節點的字放進 Hash 表以提高比對速度。處理速度較快、稍加變形即可適應各種過濾場景,缺點是用 Hash 表會浪費部分記憶體空間(敏感詞數量不多時可以接受)。
- 加降噪預處理:為了繞過敏感詞檢查,某些輸入會做手腳,例如「阿拉_伯」,這時要先對資訊做降噪預處理再比對。
B. 分類演算法(識別垃圾資訊)
- 早期主要靠人工審核,但大型網站(如 Facebook、LinkedIn 這類 Web2.0 社交網站)每天使用者提交的資訊數千萬計;B2B 撮合網站的站內信也常被廣告淹沒正常詢盤——海量資訊人工審核不現實。
- 訓練流程:先把批量已分類的樣本(書中舉例為 50000 封正常郵件、2000 封垃圾郵件)輸入分類演算法訓練,得到垃圾郵件分類模型;再用分類演算法結合分類模型辨識待處理郵件。
- 從貝氏開始:貝氏分類演算法是用機率統計方法進行分類。根據已分類樣本取得一組特徵值的機率——例如「茶葉」這個詞出現在垃圾郵件中的機率為 20%、出現在非垃圾郵件中的機率為 1%,即得到分類模型;對待處理郵件擷取特徵值後結合模型即可判斷分類。
- 演算法升級路線:貝氏假設特徵值之間互相獨立,所以又叫樸素貝氏(書中寫作 Native Bayes);但這個假設很多時候不成立,特徵值之間具有關聯性——對樸素貝氏增加特徵值的關聯依賴處理,得到 TAN 演算法;更進一步,對關聯規則做聚類挖掘,得到更強大的 ARCS(Association Rule Clustering System)演算法。
- 但先別急著升級:書中明說,由於貝氏分類演算法簡單、處理速度快,仍是許多即時線上系統反垃圾的首選。
- 同一套也能拿來分類資訊:入口網站可用它把採集來的新聞稿件自動分類、分發到不同頻道;郵箱服務商也用它依郵件內容推送個人化廣告以提高投遞相關度。
C. 黑名單(擋已知的壞來源)
- 把被舉報的垃圾郵箱地址放進黑名單,再針對郵件的發件人在黑名單中查找,找到就過濾。黑名單也可用於資訊去重,例如把文章標題或關鍵段落記入黑名單,減少搜尋引擎收錄重複資訊。
- 先用 Hash 表:實作簡單、時間複雜度小,滿足一般場景。
- 算一下記憶體再決定——書中算例:處理 10 億個黑名單郵件地址、每個地址需要 8 個位元組的資訊指紋,即需要 8GB 記憶體;為減少 Hash 衝突還需要空間冗餘,假如空間利用率為 50%,則需要 16GB 記憶體。列表越大一般伺服器越無法承受,而且衝突越多、檢索速度越慢。
- 過濾需求不要求完全精確時,改用布隆過濾器(Bloom Filter,以發明者巴頓・布隆命名):由一個二進位列表與一組隨機數映射函數實現。仍以 10 億郵件地址黑名單為例,在記憶體中建立一個 2GB 大小的儲存空間並全部初始化為 0;
- 加入黑名單:用 **8 個隨機映射函數(F1, F2, …, F8)**得到 8 個隨機數,把該郵箱地址映射到二進位儲存空間的 8 個位置,然後把這些位置置 1。
- 檢查是否在黑名單:用同樣的映射函數取得 8 個位置上的 bit,如果這些值都為 1,那麼該郵箱地址在黑名單中。
- 書中結論:處理同樣數量的資訊,布隆過濾器只使用 Hash 表所需記憶體的 1/8。
D. 電子商務風險控制
- 先分清楚在防哪一類風險(書中四類):
- 帳戶風險:帳戶被駭客盜用、惡意註冊帳號。
- 買家風險:買家惡意下單占用庫存進行不正當競爭;黃牛利用促銷搶購低價商品;此外還有良品拒收、欺詐退款,以及常見於 B2B 交易的虛假詢盤。
- 賣家風險:不良賣家惡意欺詐,例如貨不對板、虛假發貨、炒作信用;此外還有出售違禁商品、侵權產品。
- 交易風險:信用卡盜刷、支付欺詐、洗錢套現。
- 機器 + 人工雙軌:大型電商網站都配備專門的風控團隊,風控手段包括自動與人工兩種。機器自動識別為高風險的交易與資訊,會送給風控審核人員進行人工審核;機器自動風控的技術與方法,也不斷透過人工發現的新風險類型逐步完善。
- 手段一:規則引擎。當交易的某些指標滿足一定條件就被認為有高風險欺詐可能,例如:使用者來自欺詐高發地區、交易金額超過某個數值、與上次登入的地址距離差距很大、使用者登入地與收貨地不符、使用者第一次交易等。大型網站在營運過程中結合業界最新發現,會總結出數以千計的此類高風險交易規則。
- 若在業務邏輯中用 if-else 寫死,程式碼會非常龐大,而且新風險類型不斷出現、規則要不斷調整,程式碼也得跟著一直改。
- 規則引擎是一種將業務規則與規則處理邏輯相分離的技術:業務規則檔由營運人員透過管理介面編輯,需要修改規則時無需更改程式碼、發布程式即可即時使用新規則;規則處理邏輯則負責呼叫規則處理輸入的資料。
- 手段二:統計模型。規則引擎技術雖簡單,但隨著規則逐漸增加會出現規則衝突、難以維護,而且規則越多效能越差;目前大型網站更傾向使用統計模型做風控。
- 做法:使用前述分類演算法或更複雜的機器學習演算法做智慧統計——根據歷史交易中的欺詐交易資訊訓練分類演算法,再把採集加工後的交易資訊輸入,即可得到交易風險分值。
- 書中結論:經過充分訓練後的統計模型,準確率不低於規則引擎,分類演算法的即時計算效能更好;由於統計模型使用模糊識別、並不精確比對欺詐類型規則,因此對新出現的交易欺詐還具有一定預測性。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 布隆過濾器可能誤判:檢查結果說在黑名單中,但實際卻從未放入過——因為一個地址映射的 8 個 bit 可能正好都被其他地址設為 1。這種可能性極小、通常在系統可接受範圍內,但需要精確判斷時就不適合使用布隆過濾器。
- 分類演算法一定會有誤判與漏判:貝氏得到的是機率值,會有誤判(非垃圾郵件判成垃圾郵件)與漏判(垃圾郵件判成非垃圾郵件)。所以高風險結果要接人工審核,而不是直接處決。
- 正則不要硬撐:敏感詞多、文字長、並發高時,正則的效率會先垮,該換 Trie 變種或多級 Hash 表。
- 敏感詞比對不做降噪等於白做:「阿拉_伯」這類手腳會直接繞過。
- 規則引擎不是終點:規則數量上千之後衝突、維護與效能都會惡化。
- Hash 表黑名單有記憶體天花板:先按 10 億 × 8 位元組 ÷ 50% 利用率 = 16GB 這種方式算一遍,再決定資料結構。
🔗 相關工具
- 工具-資料加密與密鑰管理 —— 同一章的資料安全篇:內容與交易守住之後,靜態資料靠加密守
- 工具-網站應用攻擊與防禦 —— 同一章的攻擊防禦篇,和反垃圾同屬「使用者輸入不可信」的處理
- 工具-從規則到統計 —— 規則引擎 →統計模型正是這條路線的教科書級案例
- 工具-用資訊消除不確定性 —— 貝氏與特徵值機率背後的資訊觀
- 工具-資料結構的選擇 —— Trie/Hash 表/布隆過濾器之間的取捨思路
- 工具-時間複雜度分析 —— 判斷正則撐不撐得住、比對要不要換演算法的量尺
- 大型網站技術架構 —— 來源書
🧰 這本書給我的工具
- 工具-網站架構模式九式 — 要設計新系統、想先看一遍有哪些招可以用時
- 工具-網站高可用與伸縮性設計 — 要讓網站扛住高流量、少當機時
- 工具-秒殺系統設計 — 要辦限量搶購、瞬間湧入數百倍流量時
- 工具-網站應用攻擊與防禦 — 開放使用者輸入或上傳任何東西時
- 工具-資料加密與密鑰管理 — 要保護敏感資料、決定金鑰放哪時
- 工具-反垃圾與電商風控 — 要用機器擋垃圾內容或高風險交易時
- 工具-生產故障反模式清單 — 上線前想再檢查一遍、或覺得「這種小事不會出問題」時
分流、分區、演進等與既有書共用,不重造卡:工具-透明多級分流、工具-資料分區、工具-資料複製、工具-架構隨業務規模演進、工具-快取與資源優化、工具-可靠可擴展可維護。
✨ 關鍵重點(Layer 1–2)
- 五大架構要素:性能、可用性、伸縮性、擴展性、安全性——設計時逐項檢視。書中說知名大型網站的可用性可做到 4 個 9 以上(>99.99%),而高可用架構設計的前提就是「必然會出現伺服器宕機」。
- 九種架構模式:分層、分割、分散式、叢集、快取、非同步、冗餘、自動化、安全。書中兩條硬規定:即使存取量很小的服務也至少部署兩台伺服器構成叢集;快取只有在「熱點不均衡」且「一段時間內有效」兩個前提成立時才成立。
- 分散式不是預設答案:它帶來網路效能損耗、宕機機率上升、資料一致性困難、依賴錯綜複雜四個代價——「切莫為了分散式而分散式」。
- 秒殺是專用系統,不是調參數:以 1 萬人/10,000 並發為例,商品頁 200K × 10,000 = 2G 頻寬要另外跟運營商租;獨立部署、頁面靜態化、隨機下單 URL;下單層 10 台伺服器每台只收 10 個請求,其餘直接看結束頁。
- 安全是每天的事:全球約 70% 的 Web 應用攻擊來自 XSS 與 SQL 注入;防注入最好的方法是參數綁定(預編譯);金鑰不能寫在原始碼或設定檔裡,書中的方案是切片後分存於不同介質、由申請者/管理者/稽核者分權,沒有人能看到完整金鑰。
- 故障才是架構師的學費:第 13 章九個真實故障(log level 開 Debug 撐爆 100GB 硬碟、關掉十幾台 Memcached 導致全站癱瘓、Apache 比 JBoss 早起來、註解掉的快取程式碼被送上線……)比任何理論都具體。
💬 金句原文(Layer 0)
- 「不恰當地使用模式只會畫虎不成反類犬,不但沒有解決原來的老問題,反而帶來了更棘手的新問題。好的設計絕對不是模仿,不是生搬硬套某個模式,而是對問題深刻理解之上的創造與創新。」(2.3 小結,書 p.25)
- 「這個世界沒有絕對的安全,正如沒有絕對的自由一樣。網站的相對安全是通過提高攻擊門檻達到的。」(8.5 小結,書 p.153)
- 「大型網站的架構師最有價值的地方不在於他們掌握了多少技術,而在於他們經歷過多少故障。」(第 13 章開篇,書 p.183)
- 「軟體設計有兩種風格,一種是將軟體設計得很複雜,以使其缺陷沒那麼明顯;一種是將軟體設計得很簡單,以使其沒有明顯的缺陷。」(13.10 小結引一位軟體技術前輩,書 p.188)