---
# System prepended metadata

title: 從零開始學架構

---

# 從零開始學架構

## 架構設計的目的

定義需求

高可用，高性能，高擴展不可俱全，按照需求調整

高可能評估可接受的停機時間為多久
高性能評估預計的使用量有多少
高擴展評估之後更新的可能性有多少

## 高可用

高可用指“系统无中断地执行其功能”的能力

HA 不太可能做到絕對的 100%

### 計算高可用

指在不同機器上，相同輸入都會得到相同輸出
(由於我們的伺服器都是狀態機，所以還包含相同狀態相同輸入才會有相同輸出)

系統拆分能夠提高效能，前提是系統之間溝通的成本不會大於運算的成本。

### 存儲高可用

CAP 定理說存储高可用不可能同时满足“一致性、可用性、分区容错性”

### 高可用狀態決策

如何判斷狀態是否可用

#### 獨裁式

單一決策個體
![](https://i.imgur.com/PJTiZFg.png)

##### 優點
沒有決策混亂

##### 缺點
決策者無法做到 HA

#### 協商式

主備決策
![](https://i.imgur.com/bR5qDQ6.png)

##### 優點
決策本身可以 HA
##### 缺點
但誰主誰備是個永遠的難題

#### 民主式

ZooKeeper 決策 leader 的方式
注意如果選舉時有偶數台可能會活鎖，需要有效節點數量在 cluster 的一半以上才能正常運作(避免腦裂)。

![](https://i.imgur.com/4BK8nkZ.png)

##### 優點
可靠性高

##### 缺點
實作複雜，有最小節點數量的限制

## 可擴展性

### 預測變化

1. 不能每個設計都考慮可擴展性
2. 不能完全不考慮可擴展性
3. 預測有可能出錯

常見是區分變化層 and 穩定層

![](https://i.imgur.com/duyPHqb.png)

當然我覺得中間應該還有一層變化層，就是類似 decoder 之類的
困難在於中間接口的設計

![](https://i.imgur.com/J4l7FNY.png)

其實就是物件導向中組合的概念，把很多 Component 的實作組合到 Decorator 之中

## 成本

大公司有特殊需求才要開發新技術，小公司用現成的就好


## 安全

分成功能安全跟架構安全

## 架構設計原則

### 合適原則

> 合適優於業界領先

避免技術情節，腳踏實地

1. 合理安排工作量
有多少人手做多少事

2. 累積經驗逐步改善

3. 需求驅動進步

### 簡單原則

> 間單優於複雜

#### 計算可用度


##### 結構複雜性
三個組件的系統
![](https://i.imgur.com/R2oZ3my.png)
假設 10% 機率故障，可用度為

(1 - 0.1)^3 = 72.9%

五個組件的系統
![](https://i.imgur.com/r2kSE3M.png)

(1 - 0.1)^5 = 59%


##### 邏輯複雜性
相對的，如果我們把三個組件的功能全部塞到同一個組件，結構複雜性會降低，但單一組件的邏輯複雜性會提昇。
邏輯複雜性高一樣會導致開發上，除錯上的困難。
所以讓邏輯複雜性跟結構複雜性能取得一個平衡很重要。


### 演化原則

> 演化優於一步到位

幾個思考順序

1. 必須能滿足當下業務需求
2. 必須能迭代，去蕪存菁
3. 就算架構重寫，有價值的經驗要能延續

## 架構設計流程

1. 識別複雜度
針對需求的複雜度來設計解決方案
不要一開始就覺得自己一定要做好做滿做完美

2. 設計備選方案
最好 3 ~ 5 個
不用太詳細，大方向優缺點比較一下即可

3. 多方探討，避免只考慮到自己會的

一些細節可以定義
如何避免架構沒有設計的太過詳細 -> 一分鐘內可以敘述完
避免太多細節讓延伸討論永無止盡

### 如何評估方案

常见的方案质 属性点有：性能、可用性、硬件成本、项目投入、复杂度、安全性、可扩
展性等。

通常會以達成後一年內能否符合需求來規劃

範例架構 1

![](https://i.imgur.com/5wVhCtT.png)

範例架構 2
![](https://i.imgur.com/TtiXRaS.png)


分析範例如圖

![](https://i.imgur.com/h1kDa7R.png)

沒有說選誰一定對或是錯，但就未來性來講集群最終是得換成拆分的，所以就算現在用集群方案解決了事情。
最好也要一起準備未來的拆分方案要怎麼走，可以提早招聘人員來處理這一塊。

這就是所謂的技術債 la

### 決定技術細節

table 怎麼建，索引怎麼用，搜尋怎麼搜
nginx 的 load balance 怎麼做，做 poll, 權重, hash 的哪個？

各有利弊

poll -> 輪流

權重 -> poll 的權重版，有權重高的會多輪一些，用於伺服器負載量不平均的時候

ip hash -> 用來處理 session 的狀況(不過如果有 cookie 的話應該改成對 cookie 做 hash)

fair -> 按照後端服務器回應速度分配，最大化平衡後端服務器的負載量，但我覺得這樣做會有問題 -> (每次收到連線都 ping 一次，那大量流量湧入時會直接 ping 掛所有伺服器，如果改成定期 ping 一次，那大量連線同時湧入時負載會不均衡。)

url hash
如果有對 url 做 cache 時使用

* 架構師自己要對選用的方案有理解
* 一步一步分 步驟、階段、系統，降低方案複雜度，方案本身複雜度越高，在細節上撞車的可能性就越高。
* 如果方案複雜的話由團隊來設計，防止少數人設計時容易出現的思考盲區。

## RMDB

### 讀寫分離

![](https://i.imgur.com/XWRa2dj.png)

通常一主多從，只往主寫入，主會定期同步到從機，讀取從從機讀取。

主從中間同步會需要時間，可能從機讀不到。

衍生出二次讀取的作法，從機抓不到才從主機抓，但這樣又有另外一個問題
如何確定從機的是最新的狀態？

所以有可能會再按照業務區分可延遲的做讀寫分離，不可延遲的再從主機抓，盡量減少主機的負擔。

### 分庫分表

讀寫分離降低讀寫壓力，但沒有減少儲存壓力，數據量太大會有以下問題。

1. 讀寫性能下降，就算有索引還是會下降
2. 數據文件變大，備份跟恢復都會花很多時間
3. 數據文件保留越多，發生意外丟失的風險越大

由於以上原因，會盡量減少單個數據庫的數據量

#### 分庫

不要太早做這件事，因為後遺症很大，包含無法用 Join ， transaction，之類的功能。
單個數據庫的服務量大概在十萬用戶左右，真的有需求再來處理。

#### 分表

##### 水平分表
水平通常是某個表的數據量太大，從中間水平切割，分成 1 ~ 500, 501 ~ 1000
通常表的大小超過五千萬就要準備切了，切下去可能會有以下問題

* 路由
必須要能區分是屬於哪個表的資料，如何分段也是個學問，導致分佈不平均也會是個問題
* 範圍路由
用一個寫死的區間去切，可能會導致分段不平均。
* Hash 路由
分配均勻，但增加表的數量會有問題，那就用 constant hash 就沒問題 la
* 配置路由
需要多查詢一次，而且路由表也可能太大。

在 count, max, order by, join 操作上都會變麻煩

##### 垂直分表

拆出不常用的欄位，提昇效能，相對的就是複雜操作時，操作表的數量會增加。

### 讀寫分離

![](https://i.imgur.com/4qwJsUj.png)

通常包裝在 lib 內處理這一塊，