架構設計(一)

架構

系統: 一羣有關聯的個體 , 規則, 能力(產生了新能力)

子系統

模塊:邏輯角度 -> 組件複用

組件: 物理角度 -> 單元分離

框架: 組件規範:mvc,等

架構:結構

 

1. 軟件架構:指軟件系統的頂層結構。

首先,系統是一羣關聯的個體組成,這些個體可以是子系統, 模塊, 組件等;

其次, 系統中的個體需要根據某種規則運作, 架構需要明確個體運作和協作的規則;

第三,頂層結構的說法可以更好的區分系統和子系統。

2. 歷史背景

三難

機器語言 -> 彙編語言 -> 高級語言

3.目的

解決軟件系統複雜度的問題。

找複雜點

性能, 高可用, 擴展性, 存儲高可靠, 安全性,成本。

4. 複雜度來源:高性能

單機:操作系統

多機:任務分配, 任務分解

5. 複雜度來源:高可用   ->  冗餘

無中斷

計算高可用:

  1. 任務分配器
  2. 連接(與業務處理器)
  3. 算法(分配)

存儲高可用:

傳輸線路(故障)

 

狀態決策:

  • 獨裁式
  • 協商式
  • 民主式: ->  腦裂 -> 解決:選舉超一半

6. 複雜度來源:可擴展性

  1. 正確預測變化
  2. 完美封裝變化

變化層, 接口穩定層

抽象層, 實現層

7. 複雜度來源:低成本, 安全, 規模

低成本 -> 附加約束

  • 引入新事物
  • 創造新事物

安全

  • 功能安全:SQL注入, 代碼漏洞
  • 架構安全: 防火牆

規模: ->  量變引起質變 而帶來的複雜度:

  • 功能越來越多,系統複雜度指數增長
  • 數據越來越多,系統複雜度發生質變

8. 架構設計原則

選擇(不確定性)

  1. 合適原則: 合適由於業界領先
  2. 簡單原則:簡單優於複雜(結構複雜, 邏輯複雜,算法複雜)
  3. 演化原則:演化由於一步到位

9. 案例

 

10. 架構設計流程:識別複雜度

列出複雜度問題,排序依次解決;

理解需求,找複雜度 -> 可用排查法,從不同角度逐一分析

每秒TPS , 平均值, 峯值(2-4倍平均值)

設計目標用峯值計算。

複雜度

  • 高性能
  • 高可用
  • 擴展性

TPS:寫入

QPS:查詢

eg.  目前TPS 是115, QPS是1150,

則峯值 x 3, TPS是345, QPS是3450  ->  這個量級不要求高性能;

預留後續發展, 按 峯值 x 4, TPS是 1380, QPS是13800 -> 高性能

不同的公司會有不同的複雜度分析: 安全,或成本

eg. 消息隊列

前浪微博:複雜度體現在

  • 高性能消息讀取
  • 高性能消息寫入
  • 高可用消息存儲
  • 高可用消息讀取

11. 架構流程設計:設計備選方案

常見錯誤:

  1.設計最優秀的方案

  2. 只做一個方案

應設計3-5個備選方案。

備選方案差異要比較明顯

備選方案的技術不要只限於已熟悉的技術

備選方案不用過於詳細 ->  應關注技術選型,而不是細節

eg. 設計備選方案 - > 高性能讀取,寫入,高可用存儲,讀取。

  1. 用kafka

  2. 集羣 + mysql 存儲

高性能讀取,寫入 ->  Java, 基於netty開發消息隊列

高可用存儲,讀取 ->  服務器的主備方案, mysql的主備複製。

  3. 集羣 + 自研存儲方案

可參考kafka,自己實現一套存儲和複製方案。

12. 架構設計流程:評估和選擇備選方案

  • 最簡派
  • 最牛派
  • 最熟派
  • 領導派

分場景使用

  1. 360度環評表: 列出我們需要關注的質量屬性點,分別從這些點的維度評估每個方案,再綜合選合適當時情況的最優方案。

常見的方案質量屬性點:性能,可用性, 硬件成本, 項目投入,複雜度,安全性,可擴展性,可維護性等。

  2. 按優先級選擇

13. 架構設計流程: 詳細方案設計

確定方案 的關鍵細節

詳細設計方案階段,可能遇到一種極端情況,發現備選方案不可行。

一般情況下,主要原因是涉及備選方案時遺漏了關鍵技術點或關鍵質量屬性。

這種情況下,可通過以下方式避免:

  1. 架構師不但要備選方案的設計和選型,還要對備選方案的關鍵細節有深入理解
  2. 通過分步驟,分階段,分系統,降低複雜度
  3. 如果方案本身複雜,可採取設計團隊,博採衆長,彙集大家的智慧。

14. 高性能數據庫:讀寫分離

本質:將訪問壓力分散到集羣中的多個節點,但沒分散存儲壓力。

基本原理:將數據庫讀寫操作分散到不同的節點上。

主 + 從  集羣: 主負責讀/寫,從負責讀。

不同於 主 + 備

基本實現:

  • 數據庫服務器,搭建主從集羣,一主一從或一主多從 都可以。
  • 主機負責讀寫,從機負責讀
  • 數據庫主機通過複製將數據同步到從機,每個數據庫都存了所有數據
  • 業務服務器將寫發給主數據庫,讀發給從數據庫。

有兩個複雜點:1. 主從複製延遲; 2. 分配機制

1. 複製延遲

先假設延遲1秒,數據寫入主庫後立刻訪問從庫,讀取不到最新數,例如註冊,會有業務問題。

它的常規解決方法:

  1. 寫操作後的讀操作指定發主數據庫 ->  和業務強綁定,對業務侵入和影響較大
  2. 讀從機失敗後,再讀一次主機  ->  即二次讀取,無業務綁定,只需對底層數據庫的訪問封裝,代價小,但若有很多二次讀取,將增加主機的讀取壓力
  3. 關鍵業務讀寫都指向主機,非關鍵業務讀寫分離

2. 分配機制

將讀寫區分,訪問不同的數據庫,一般有兩種方式:程序代碼封裝和中間件封裝

  2.1 程序代碼封裝:在代碼中抽象一個數據訪問層,實現讀寫分離和數據庫連接管理。

特點:實現簡單,可根據業務做定製化功能;

每個語言實現一次;

故障情況下,如果主從發生切換,則需要所有系統都修改配置並重啓;

eg. 淘寶的 TDDL(Taobao Distributed Data Layer)

基本原理:基於集中式的配置Jdbc datasource實現: 有主備,讀寫分離

基本架構

 

   2.2 中間件封裝

指獨立出一套系統,實現讀寫分離和數據庫服務器連接的管理。

一般建議,程序語言封裝或用成熟中間件

Mysql Router, 

Atlas : 基於Mysql Proxy

 

思考:

讀寫分離一般用於實現什麼場景?支撐多的業務規模?

讀多寫少,實時性要求不高

15. 高性能數據庫集羣:分庫分表

分散存儲

數據量 從千萬到億 , 就會有單臺瓶頸。

業務分庫

按業務分到不同的數據庫 ->  分庫能支撐百萬甚至千萬規模業務。

存在問題:join 問題(不同庫), 事務, 成本等。

分表

垂直拆分, 水平拆分

垂直:

 

水平:

複雜性

  1. 路由 - 範圍路由(分佈不均勻), hash路由(分佈均勻), 配置路由(用單獨的表記錄路由信息)

  2. join

  3. count (count相加,記錄數表)

  4. order by 

實現方式:代碼封裝,中間件封裝

16. 高性能NoSQL 

not only SQL, 彌補數據庫缺陷

關係數據庫缺點:

  1. 存儲的行記錄,無法存數據結構 - 無法直接存列表
  2. schema 擴展不方便: 操作不存在的列會報錯,擴充列要DDL,麻煩
  3. 大數據場景下, I/O較高; 單列統計 ,會讀取整行
  4. 全文搜索功能較弱; like 整表掃描, 性能低

NoSQL分4類

  • k-v存儲:解決無法存數據結構的問題, 以Redis爲代表
  • 文檔數據庫:解決強schema約束問題,以MongoDB爲代表
  • 列式數據庫:解決大數據場景下I/O問題,以HBase爲代表
  • 全文搜索索引:解決全文搜索性能問題,以Elasticsearch爲代表

Redis

 eg, lpop 移除list第一個元素。

缺點:不支持完整的ACID, 只保證隔離性和一致性,無法保證原子性和持久性。

文檔數據庫

特點:no-schema,可存儲和讀取任意數據,大部分是Json格式存儲

優點:

  • 新增字段簡單
  • 歷史數據不會出錯
  • 更易存儲複雜數據

適合場景:電商和遊戲

缺點:不支持事務,無法join操作。

列式數據庫

對比,關係數據庫,行式存儲:讀多列,效率高,能一次完成對一行對個列寫操作。

列式數據庫:對某個列統計,節省I/O;有更高的壓縮比(行:3:1 - 5:1; 列: 8:1, 30:1)

一般將列式存儲用在大數據分析和統計場景;主要針對部分單列操作,且寫入後無需再更新,刪除;

全文搜索引擎

基本原理:倒排索引,是一個索引方法,是建立單詞到文檔的索引。

使用方式:將關係數據轉換爲文檔數據Json。

Elasticsearch是分佈式文檔存儲方式,每個字段的所有數據默認被索引。

17.高性能緩存架構

某些複雜場景,單靠存儲系統,性能提升是不夠的。

  a. 要經過複雜運算後得到的數據

  b. 讀多寫少的數據

緩存-基本原理:將可能重複使用的數據放到內存,一次生成,多次使用,避免每次使用都訪問存儲系統。

mechache 單臺支持50000 TPS以上。

緩存-架構設計要點:

  1. 緩存穿透:指緩存中沒數據,都查了存儲系統。

有兩種情況:

  a. 存儲數據不存在  -  解決:可設置一個默認值

  b. 緩存數據生成耗費大量的時間或資源

  2. 緩存雪崩:指緩存失效後,引起系統性能急劇下降的情況

解決:

  a. 更新鎖機制:對緩存更新操作加鎖保護,集羣時需要設置分佈式鎖。

  b. 後臺更新:由後臺線程更新緩存,而不是業務線程;緩存本身設置有效期永久,後臺定時更新;

    當內存不足時,會踢掉數據,可有兩種辦法:

      1>. 後臺除定時更新緩存,還要頻繁讀緩存

      2>. 業務線程發現緩存失效,通過消息隊列發一條消息通知後臺線程更新緩存。

還可以用後臺更新進行緩存預熱。

  3. 緩存熱點

   對於一些特別熱點的數據,大部分業務請求都命中同一份緩存數據,則這份數據所在的緩存服務器壓力也很大。

緩存熱點的解決方案是:複製多份緩存副本,將請求分散到多個緩存服務器,減輕緩存熱點導致後臺服務器壓力。

一個細節要注意:不同的緩存副本不要設置統一的過期時間,應設置一個過期時間範圍,不同副本過期時間是指定範圍的隨機值。

實現方式:

  • 程序代碼的中間層方式實現
  • 獨立中間件實現

18. 單服務器高性能模式:PPC與TPC

磁盤,操作系統,cpu,內存,網絡,編程語言,架構, 都可能影響達到高性能。

架構師要考慮:高性能架構的設計

集中兩個點

  • 儘量提升單服務性能,將單服務器性能發揮到極致
  • 如果單臺服務器無法支持性能,設置服務器集羣方案

還和編碼有關

架構設計決定系統性能的上限,實現細節決定下限。

單服務器性能關鍵之一:服務器採用併發模型。

關鍵設計點:

  • 服務器如何管理連接
  • 服務器如何處理請求

這兩個設計點都和操作系統的I/O模型及進程模型相關。

I/O模型: 阻塞, 非阻塞, 同步,異步

進程模型: 單進程, 多進程, 多線程

單服務器高性能模式:PPC, TPC

PPC

: process per connection ,指每次有新的連接就新建一個進程,去專門處理這個連接的請求。

 

 適合連接沒那麼多的情況。

缺點:

  1. fork代價高
  2. 父子進程通信複雜
  3. 支持的併發連接數量有限

一般情況下,PPC最多能處理的併發連接數就幾百。

prefork  ->  提前創建進程

TPC

Thread per connection. 每次有新的連接就新建一個線程,專門處理這個連接的請求。

TPC基本流程:

TCP雖然解決了fork代價高和進程通信複雜的問題,但有其他問題:

  1. 高併發性能問題
  2. 沒有進程僅通信,但線程間的互斥和共享,易導致死鎖
  3. 多線程相互影響,一個線程異常,整個進程退出

因此,幾百連接的場景,更多用PPC

prethread -> 提前創建線程

19.單服務器高性能模式:Reactor與Proactor

PPC和TPC無法支持高併發

Reactor

資源複用,進程池

read阻塞  ->  改爲了非阻塞,進程輪詢多個連接(當連接太多,輪詢效率低)

再改進 -> 只有連接上有數據,進程纔去處理 -> 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章