# 從零開始學架構 ## 架構設計的目的 定義需求 高可用,高性能,高擴展不可俱全,按照需求調整 高可能評估可接受的停機時間為多久 高性能評估預計的使用量有多少 高擴展評估之後更新的可能性有多少 ## 高可用 高可用指“系统无中断地执行其功能”的能力 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 內處理這一塊,