Skip to main content
系統的三大支柱
  1. Posts/

系統的三大支柱

·6 mins
Table of Contents

我們為什要這樣設計資料系統?

在金融、電商、社交平台等產業裡,多數的應用系統屬於資料密集型(data-intensive),常遇到的瓶頸在於資料量級、資料複雜度、及快速的變化性,而不是CPU的處理能力。

在非功能性需求方面,對大多數軟體系統來說都極為重要的三個面向有:可靠性(Reliability)、可擴展性(Scalability)、可維護性(Maintainability)。


可靠性
#

系統出錯時仍能正常運作。

故障(faults) 可能來自硬體故障、軟體錯誤、人為失誤:

  • 硬體故障:硬碟壞軌、記憶體掛點、停電等。解決方法通常是幫硬體元件添加冗餘(redundancy),類似備援的概念。當一個元件陣亡,冗餘元件可以馬上取代它,例如:資料中心(datacenter)可能規劃柴油發電機和電池作為備用電源。
  • 軟體錯誤:硬體故障常是相互獨立的,很少出現大量硬體同時故障。但軟體中的系統錯誤,常因為節點之間有相互關聯,導致更多的系統故障。例如:一個Linux kernel裡的bug導致許多應用程式當機、一個程序(process)吃掉了許多共享資源如CPU、memory、磁碟空間、網路寬頻。軟體系統的故障沒有快速解法,但我們能仔細審視系統架構間的互動、全面性的測試、監測及分析正式環境的系統行為等,例如:檢查message queue中的訊息量是否有異常。
  • 人為失誤:人無法做到萬無一失,難免維運系統時將config設錯等。我們可以將容易出錯的地方隔離出來,使用真實資料在沙盒(sandbox)等獨立環境做探索和嘗試。建立可以快速回滾(roll back)的機制、或滾動發佈程式碼,都可以降低意外故障的影響。

可靠性有多重要?可以預測並優雅應對故障的系統稱為容錯(fault-tolerant)或彈性(resilient)的系統。對一個平凡user(我)而言,如果我將所有與家人的照片與影片存在某個APP,結果它資料庫壞了,我應該會崩潰。

可擴展性
#

系統能處理增長的負載(load)。

當負載增加,資源不變(CPU、記憶體、網路寬頻),系統性能會有什麼影響?
當負載增加,若要保持系統性能不變,需要增加多少資源?
  • 負載:依不同系統架構,可以用不同的負載參數(load parameter)來描述負載,例如:對server的每秒請求次數(Requests per second)、資料庫的讀寫比例、購物網站的同時活躍用戶(simultaneously active users)、快取命中率等。
  • 性能: 指標如吞吐量(Throughput)指單位時間內系統能處理的工作量,像批次處理系統Hadoop每秒可以處理幾筆資料?或處理特定大小的dataset需要多久時間?對於線上系統,服務的回應時間(response time)通常更重要,客戶端從發出請求到收到回應需要多久?

面對增長的負載,常見的應對方式有 垂直擴展(scale up, vertical scaling) 即升級到更強大的機器上,比如Oracle資料庫升級為更高階的Exadata;以及 水平擴展(scale out, horizontal scaling) 即將負載分散到多台小機器上,比如MongoDB分片(sharding)將資料分到不同節點。

沒有所謂既通用又適合所有應用的可擴展架構(magic scaling sauce)。大規模運行的系統,它的架構通常是依照其特定應用的需求去量身打造。要考慮的因素很多,例如:讀/寫量級、資料儲存量級、資料複雜度、回應速度要求、存取模式等。目的都是讓系統能在長大後依然健康,而不是被流量壓垮。

可擴展的系統可以按需求增加更多處理能力,在高負載下維持系統的可靠性。

回應時間是客戶端所看到的,它包含了處理請求的實際服務時間(service time)、網路延遲(network delay)、排隊延遲(queueing delay)。
當請求處於等待服務的狀態,這段等待被處理的期間稱為延遲(latency)。

我們可以用中位數(p50)來知道user一般需要等多久。再觀察異常值,假設第99百分位數(p99)的回應時間是1秒,表示有99%的請求回應時間不到1秒、有1%超過1秒。
百分位數常用於服務水準協定(SLA),定義了客戶期望的性能,例如一個正常運行的服務平均回應時間應該低於200毫秒、且p99在1秒內。

高百分位數回應時間(尾延遲tail latencies)會直接影響user對服務的體驗。
大部分原因來自排隊延遲,由於server並行處理能利有限(例如CPU core數的限制),所以只要有幾個慢請求(slow request),就會讓後面的請求塞車,這有時稱為隊頭阻塞(Head-of-line blocking)。

可維護性
#

系統能長期被理解與修改。

即使一個系統很穩又能撐流量,但如果很難維護,很快就會變成技術債。 設計系統時可以注意的方向-可維運性、簡單性、可演化性:

  • 可維運性:讓維運團隊更容易保持軟體系統穩定運作,比如利用視覺化的監控儀表板更方便觀察系統的健康狀態,並且能有效管理它。

  • 簡單性:盡量降低系統複雜性,讓新接手的人更容易理解系統。複雜性可能來自code雜亂無章和重複、module緊密耦合、混亂的相依性、不一致的命名和術語、為特例而做的取巧做法等。複雜性會把一個軟體專案變成大泥球(big ball od mud),它們在短期內看似有效,但長久下來會使維護變得很困難,也許開發人員一個小小改動卻造成多處錯誤,每次修改都像在踩地雷。

      我們可以利用抽象(abstraction)來消除「意外的」複雜性,一個好的抽象設計,將大量實作細節隱藏在一個乾淨易懂的表面下。
      例如:SQL,它隱藏了複雜的磁碟和記憶體資料結構、與來自其他客戶端的併發請求等。
    
  • 可演化性(可延展性、可修改性、可塑性):讓開發人員在未來能輕鬆改進與調整系統。很少有永遠不變的系統,它可能會遇到各種新需求,比如擴充新功能、法規變動、新舊平台交替等。與前述簡單性相關,簡單易懂的系統通常更容易修改以適應不斷變化的需求。而敏捷(Agile)開發模式、測試驅動開發(TDD)和重構(refactoring)等方法,也能帶來幫助。

可維護性的本質是要讓工程和維運團隊朝無痛維運邁進。在做軟體設計時多加考慮,盡量最小化系統維護的痛苦。它也許不會立刻讓系統變好,但會影響未來三、五年的成本。


想像可靠性是底線,維持基本的穩定度;可擴展性是能力,能承載更大的世界;可維護性是壽命,希望活得健康長久。 在寫程式和設計架構時謹記這三個原則,能兼顧三者的系統才能走得長遠。

Reply by Email