複雜度管理#
架構設計的主要目的是為了解決軟件系統複雜度帶來的問題。理解複雜度的來源和管理方法,是架構師的核心能力。
架構設計的真正目的#
架構設計的主要目的是為了解決軟件系統複雜度帶來的問題。
這個看似簡單的結論,卻是架構設計的指導原則。
常見誤區#
| 誤區 | 問題 |
|---|---|
| 「架構很重要,所以要做」 | 正確的廢話,不知道為何重要 |
| 「每個系統都要做架構設計」 | 知其然不知其所以然,容易生搬硬套 |
| 「流程要求必須有架構設計」 | 舍本逐末,為了做而做 |
| 「為了高效能、高可用」 | 不管三七二十一都要「高」,容易過度設計 |
正確的理解#
明確「解決複雜度」這個目標後:
- 新手:心中有數,知道從哪裡下手
- 老鳥:有的放矢,不會貪大求全
複雜度分析實例
場景:設計一個大學學生管理系統
| 維度 | 分析 | 結論 |
|---|---|---|
| 效能 | 學生 1~2 萬,訪問頻率低 | 不複雜,MySQL + Nginx 足夠 |
| 可擴展 | 功能穩定,變化不大 | 不複雜 |
| 高可用 | 宕機 2 小時影響不大 | 不需要負載均衡、異地多活 |
| 存儲可靠 | 資料丟失修復困難 | 這是複雜點,需要主備 |
| 安全 | 有隱私但非強隱私 | ACL + 密碼管理即可 |
結論:主要複雜度在存儲可靠性,架構重點是 MySQL 主備方案。
複雜度的六個來源#
┌─────────────────────────────────────────────────────────┐
│ 複雜度來源 │
├──────────────┬──────────────┬───────────────────────────┤
│ 高效能 │ 高可用 │ 可擴展 │
├──────────────┼──────────────┼───────────────────────────┤
│ 低成本 │ 安全 │ 規模 │
└──────────────┴──────────────┴───────────────────────────┘來源一:高效能#
詳見高效能架構設計章節。
核心複雜度:
- 單機:並發模型選擇
- 集群:任務分配與分解
來源二:高可用#
詳見高可用架構設計章節。
核心複雜度:
- 冗餘方案設計
- 故障檢測與切換
- 資料一致性
來源三:可擴展#
詳見可擴展架構設計章節。
核心複雜度:
- 預測哪些會變化
- 如何隔離或封裝變化
來源四:低成本#
低成本本質上與高效能、高可用衝突,因為後者通常需要更多機器。
低成本通常不是首要目標,而是約束條件。在滿足效能和可用性的前提下,盡量降低成本。
低成本的實作途徑:
| 途徑 | 描述 | 例子 |
|---|---|---|
| 引入新技術 | 用更高效的技術替代 | Redis 替代 MySQL 做快取 |
| 創造新技術 | 自研更適合的方案 | Facebook 的 HHVM |
| 架構最佳化 | 減少不必要的組件 | 去掉過度設計的中間層 |
來源五:安全#
安全複雜度分為兩類:
功能安全(防小偷)
- 本質:系統實作有漏洞
- 例子:XSS、SQL 注入、CSRF
- 特點:逐步完善,無法一勞永逸
架構安全(防強盜)
- 本質:抵禦外部攻擊
- 例子:DDoS、入侵
- 方案:防火牆、WAF、流量清洗
互聯網系統的架構安全主要依靠運營商或雲服務商的能力,很少自己實作。
來源六:規模#
量變引起質變,當規模超過一定閾值,複雜度會發生質的變化。
功能規模的複雜度
功能數量增加時,複雜度指數級增長:
3 個功能:複雜度 = 3 + 3 = 6
●───●
\ /
●
8 個功能:複雜度 = 8 + 28 = 36
●─●─●
│╲│╱│
●─●─●
╲│╱
●─●資料規模的複雜度
| 資料量 | 可能的問題 |
|---|---|
| < 100 萬行 | 單表基本夠用 |
| 100 萬 ~ 1000 萬 | 需要索引最佳化 |
| 1000 萬 ~ 1 億 | 考慮分表 |
| > 1 億 | 必須分庫分表 |
MySQL 單表超過 5000 萬行後:
- 添加索引可能需要數小時
- 修改表結構同樣耗時
- 備份時間大幅增加
複雜度分析方法#
步驟一:識別複雜度#
不是所有系統都有所有複雜度,需要根據業務識別主要複雜度。
┌─────────────────────────────────────────────────────────┐
│ 識別複雜度 │
├─────────────────────────────────────────────────────────┤
│ 1. 這個系統最大的技術挑戰是什麼? │
│ 2. 如果這個問題解決不好,會有什麼後果? │
│ 3. 業界類似系統是怎麼解決的? │
└─────────────────────────────────────────────────────────┘步驟二:分析複雜度#
針對識別出的複雜度,深入分析其本質。
複雜度分析模板
| 分析項 | 內容 |
|---|---|
| 複雜度類型 | 高效能 / 高可用 / 可擴展 / … |
| 具體表現 | 具體是什麼問題 |
| 影響範圍 | 影響哪些功能、哪些用戶 |
| 嚴重程度 | 高 / 中 / 低 |
| 解決方案 | 備選方案及對比 |
步驟三:設計架構#
針對複雜度設計架構,而不是為了架構而架構。
flowchart TD
A[業務需求] --> B[識別複雜度來源]
B --> C[針對性設計架構]
C --> D[落地實作]
B -.- B1((高效能?高可用?可擴展?))
C -.- C1((只解決識別出的複雜度))
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#f3e5f5複雜度管理原則#
原則一:識別主要複雜度#
不要試圖解決所有問題,聚焦主要矛盾。
一個系統通常只有 1~2 個主要複雜度,解決了主要複雜度,其他問題往往迎刃而解或變得不那麼重要。
原則二:按需設計#
需要多少複雜度就設計多少,不多也不少。
| 情況 | 問題 | 後果 |
|---|---|---|
| 過度設計 | 解決了不存在的問題 | 浪費資源,引入不必要複雜度 |
| 設計不足 | 忽略了存在的問題 | 上線後出問題,被迫重構 |
原則三:適度前瞻#
參考「2 年法則」,適度考慮未來,但不要過度。
過去 現在 未來
│ │ │
───────┼────────────────┼────────────────┼───────
│ │ │
│ 設計基於 │ 考慮範圍 │
│ 當前需求 │ (2年內) │原則四:持續演進#
架構不是一成不變的,需要隨業務發展而演進。
flowchart LR
subgraph 業務初期
A[單體應用]
end
subgraph 業務發展
B[垂直拆分<br/>SOA]
end
subgraph 業務成熟
C[微服務<br/>+ 中台]
end
A --> B --> C
style A fill:#ffecb3
style B fill:#c8e6c9
style C fill:#bbdefb常見複雜度場景#
場景一:初創業務#
特點:需求不確定,變化快,團隊小
主要複雜度:可擴展性(適應快速變化)
建議架構:
- 單體應用,快速迭代
- 保持程式碼整潔,方便後續拆分
- 不要過早微服務化
場景二:快速增長#
特點:用戶量快速增長,系統壓力大
主要複雜度:高效能
建議架構:
- 加快取
- 讀寫分離
- 按需拆分服務
場景三:成熟業務#
特點:業務穩定,對可靠性要求高
主要複雜度:高可用、運維效率
建議架構:
- 完善監控告警
- 故障自動恢復
- 灰度發布能力
場景四:企業級應用#
特點:功能複雜,集成需求多
主要複雜度:規模複雜度、集成複雜度
建議架構:
- 領域驅動設計
- 服務化架構
- 完善的 API 管理
複雜度評估矩陣#
在做架構決策時,可以用以下矩陣評估:
| 複雜度 | 當前狀態 | 預期增長 | 優先級 | 方案 |
|---|---|---|---|---|
| 高效能 | 中 | 高 | P1 | 加快取、水平擴展 |
| 高可用 | 低 | 中 | P2 | 主從架構 |
| 可擴展 | 中 | 中 | P2 | 模塊化 |
| 低成本 | - | - | P3 | 雲服務 |
| 安全 | 低 | 低 | P3 | 基礎防護 |
| 規模 | 低 | 中 | P3 | 預留拆分空間 |
本章小結#
| 主題 | 核心觀點 |
|---|---|
| 設計目的 | 解決複雜度問題,而非為了架構而架構 |
| 六個來源 | 高效能、高可用、可擴展、低成本、安全、規模 |
| 分析方法 | 識別 → 分析 → 設計 → 落地 |
| 管理原則 | 識別主要矛盾、按需設計、適度前瞻、持續演進 |
架構 = 解決複雜度的方案 好的架構 = 用最簡單的方案解決主要複雜度
- 每次架構設計前,先問自己:「這個系統的主要複雜度是什麼?」
- 如果答不上來,說明對業務的理解還不夠深入
- 如果列出了太多複雜度,說明可能過度設計了