系統複雜度之【高性能】

来源:https://www.cnblogs.com/arcstack/archive/2023/06/23/17499518.html
-Advertisement-
Play Games

今天我們來談一談系統複雜度的根源之【高性能】 對性能的不懈追求一直是人類科技持續發展的核心動力。例如電腦,從電子管電腦到晶體管電腦,再到集成電路電腦,運算性能從每秒幾次提高到每秒幾億次。然而,隨著性能的提升,相應的方法和系統複雜度也逐漸增加。現代電腦CPU集成了數億顆晶體管,其邏輯複雜度和 ...


今天我們來談一談系統複雜度的根源之【高性能】

對性能的不懈追求一直是人類科技持續發展的核心動力。例如電腦,從電子管電腦到晶體管電腦,再到集成電路電腦,運算性能從每秒幾次提高到每秒幾億次。然而,隨著性能的提升,相應的方法和系統複雜度也逐漸增加。現代電腦CPU集成了數億顆晶體管,其邏輯複雜度和製造難度與最初的晶體管電腦相比,已經有了天壤之別。

軟體系統也呈現出類似的現象。近幾十年來,軟體系統性能得到了飛速發展,從最初的電腦僅能進行簡單科學計算,到如今Google能支撐每秒幾萬次的搜索。與此同時,軟體系統規模從單台電腦擴展到上萬台電腦;從最初的單用戶單任務的字元界面DOS操作系統,發展到現在的多用戶多任務的Windows 10圖形操作系統。

當然,技術進步帶來的性能提升,並不一定伴隨著複雜度的增長。例如,硬體存儲從紙帶、磁帶、磁碟發展到SSD,並未明顯增加系統複雜度。這是因為新技術逐步淘汰舊技術,我們可以直接使用新技術,而無需擔心系統複雜度的提升。只有那些不是用來取代舊技術,而是開拓全新領域的技術,才會給軟體系統帶來複雜性,因為軟體系統在設計時需要在這些技術之間進行判斷、選擇或組合。就像汽車的發明不能取代火車,飛機的出現也不能完全替代火車,所以在我們出行時,需要權衡選擇汽車、火車還是飛機,這個選擇過程相對複雜,涉及價格、時間、速度、舒適度等諸多因素。

軟體系統中性能提升帶來的複雜度主要體現在兩個方面:一方面是單台電腦內部為實現高性能所產生的複雜度;另一方面是多台電腦集群為實現高性能所引發的複雜度。

單機複雜度

電腦內部複雜度的關鍵在於操作系統。電腦性能的發展基本上是由硬體,尤其是CPU性能的發展所驅動的。著名的“摩爾定律”預測CPU處理能力每18個月翻一番;而充分發揮硬體性能的關鍵便是操作系統。因此,操作系統也是隨著硬體的發展而發展的,作為軟體系統的運行環境,操作系統的複雜度直接決定了軟體系統的複雜度。

操作系統與性能最相關的便是進程線程。最早的電腦實際上是沒有操作系統的,僅具有輸入、計算和輸出功能。用戶輸入一個指令後,電腦完成操作。然而,大部分時間電腦都在等待用戶輸入指令,這種處理性能顯然是低效的,因為人的輸入速度遠不及電腦的運算速度。

為解決手工操作帶來的低效,批處理操作系統應運而生。簡單來說,批處理是先將要執行的指令預先編寫好(寫到紙帶、磁帶、磁碟等),形成一個指令清單,即我們常說的“任務”。將任務交給電腦執行,批處理操作系統負責讀取“任務”中的指令清單併進行處理。這樣,電腦執行過程中無需等待人工操作,從而大大提高性能。

儘管批處理程式大大提升了處理性能,但它存在一個明顯的缺點:電腦一次只能執行一個任務。如果某個任務需要從I/O設備(例如磁帶)讀取大量數據,在I/O操作過程中,CPU實際上是空閑的,而這段空閑時間本可以用於其他計算。

為進一步提升性能,人們發明瞭“進程”,將一個任務對應到一個進程,每個任務都有自己獨立的記憶體空間,進程間互不相關,由操作系統進行調度。當時的CPU尚無多核和多線程概念,為實現多進程並行運行,採用了分時方式,即將CPU時間劃分成許多片段,每個片段僅執行某個進程中的指令。儘管從操作系統和CPU角度看仍為串列處理,但由於CPU處理速度極快,用戶感覺上是多進程並行處理。

多進程要求每個任務都有獨立的記憶體空間,進程間互不相關,但從用戶角度看,若兩個任務在運行過程中能夠通信,則任務設計會更加靈活高效。為解決這個問題,各種進程間通信方式應運而生,包括管道、消息隊列、信號量、共用存儲等。

多進程使多任務能夠並行處理,但仍存在缺陷,單個進程內部僅能串列處理。實際上,許多進程內部的子任務並不要求嚴格按時間順序執行,也需要並行處理。例如,一個餐館管理進程,排位、點菜、買單、服務員調度等子任務必須並行處理,否則可能出現因某客人買單時間較長(如信用卡刷不出來)而導致其他客人無法點菜的情況。為解決這一問題,人們發明瞭線程,線程是進程內部的子任務,但這些子任務共用同一份進程數據。為保證數據的正確性,又發明瞭互斥鎖機制。有了多線程後,操作系統調度的最小單位變成了線程,而進程則成為操作系統分配資源的最小單位。

儘管多進程多線程讓多任務並行處理的性能大大提升,但本質上仍為分時系統,無法實現時間上真正的並行。解決這一問題的方法是讓多個CPU同時執行計算任務,從而實現真正意義上的多任務並行。目前這樣的解決方案有三種:SMP(對稱多處理器結構)、NUMA(非一致存儲訪問結構)和MPP(海量並行處理結構)。其中,SMP是我們最常見的,當前流行的多核處理器即採用SMP方案。

如今的操作系統發展已經相當成熟,若要完成一個高性能的軟體系統,需要考慮多進程、多線程、進程間通信、多線程併發等技術點。然而,這些技術並非最新的就是最好的,也不是非此即彼的選擇。在進行架構設計時,需要投入大量精力來結合業務進行分析、判斷、選擇和組合,這一過程同樣頗為複雜。舉一個簡單的例子:Nginx可以採用多進程或多線程,JBoss則採用多線程;Redis採用單進程,而Memcache則採用多線程,儘管這些系統都實現了高性能,但其內部實現卻大相徑庭。

集群的複雜度

儘管電腦硬體性能迅速發展,但與業務發展速度相比,仍顯得力不從心,特別是進入互聯網時代後,業務發展速度遠超硬體發展速度。例如:

  • 2016年“雙11”支付寶每秒峰值達到12萬筆支付。

  • 2017年春節微信紅包收發紅包每秒達到76萬個。

要支持如支付和紅包等複雜業務,單機性能無論如何都無法支撐。因此,必須採用機器集群的方式來實現高性能。例如,支付寶和微信這類規模的業務系統,後臺系統的機器數量均達到了萬台級別。

通過大量機器提升性能,並非僅僅是增加機器那麼簡單。讓多台機器協同完成高性能任務是一項複雜的任務。以下針對常見的幾種方式進行簡要分析:

1.任務分配

任務分配意味著每台機器都能處理完整的業務任務,將不同任務分配給不同機器執行。為實現有效的任務分配,負載均衡技術應運而生。負載均衡可以通過硬體或軟體實現,其目標是將任務平均分配到各個機器上,確保系統資源得到充分利用,從而提高整體性能。

2.數據分片

數據分片是指將數據切分成多個部分,每個部分分配到一個或多個機器上。這種方式可以實現數據的水平擴展,提高數據處理能力。例如,資料庫分片技術可以將一個大型資料庫分割成多個小型資料庫,每個小型資料庫承擔部分數據處理任務。

3.數據副本

為提高數據可靠性和可用性,可在多台機器上創建數據的副本。當一臺機器出現故障時,其他具有數據副本的機器可以立即接管服務,確保業務不間斷。數據副本技術在分散式存儲系統中尤為重要,如分散式文件系統、分散式資料庫等。

4.任務並行

任務並行是指將一個大任務分解成多個小任務,併在多台機器上同時執行。這種方式可以顯著減少任務執行時間,提高處理能力。例如,MapReduce是一種著名的任務並行計算模型,可以將大數據處理任務分解成多個小任務,在集群中並行執行,最後將結果彙總輸出。

總之,集群技術為應對業務需求提供了強大的支持,但實現高性能集群需要考慮任務分配、數據分片、數據副本、任務並行等多種技術,並根據具體業務需求進行合理選擇和組合。隨著雲計算的普及,集群技術將更加成熟,未來亦可期待更多創新和發展。

我從最簡單的一臺伺服器變兩台伺服器開始,來講任務分配帶來的複雜性,整體架構示意圖如下。

從圖中可以看到,1台伺服器演變為2台伺服器後,架構上明顯要複雜多了,主要體現在:

  • 需要增加一個任務分配器,這個分配器可能是硬體網路設備(例如,F5、交換機等),可能是軟體網路設備(例如,LVS),也可能是負載均衡軟體(例如,Nginx、HAProxy),還可能是自己開發的系統。選擇合適的任務分配器也是一件複雜的事情,需要綜合考慮性能、成本、可維護性、可用性等各方面的因素。

  • 任務分配器和真正的業務伺服器之間有連接和交互(即圖中任務分配器到業務伺服器的連接線),需要選擇合適的連接方式,並且對連接進行管理。例如,連接建立、連接檢測、連接中斷後如何處理等。

  • 任務分配器需要增加分配演算法。例如,是採用輪詢演算法,還是按權重分配,又或者按照負載進行分配。如果按照伺服器的負載進行分配,則業務伺服器還要能夠上報自己的狀態給任務分配器。

這一大段描述,即使你可能還看不懂,但也應該感受到其中的複雜度了,更何況還要真正去實踐和實現。

上面這個架構只是最簡單地增加1台業務機器,我們假設單台業務伺服器每秒能夠處理5000次業務請求,那麼這個架構理論上能夠支撐10000次請求,實際上的性能一般按照8折計算,大約是8000次左右。

如果我們的性能要求繼續提高,假設要求每秒提升到10萬次,上面這個架構會出現什麼問題呢?是不是將業務伺服器增加到25台就可以了呢?顯然不是,因為隨著性能的增加,任務分配器本身又會成為性能瓶頸,當業務請求達到每秒10萬次的時候,單台任務分配器也不夠用了,任務分配器本身也需要擴展為多台機器,這時的架構又會演變成這個樣子。

這個架構比2台業務伺服器的架構要複雜,主要體現在:

  • 任務分配器從1台變成了多台(對應圖中的任務分配器1到任務分配器M),這個變化帶來的複雜度就是需要將不同的用戶分配到不同的任務分配器上(即圖中的虛線“用戶分配”部分),常見的方法包括DNS輪詢、智能DNS、CDN(Content Delivery Network,內容分髮網絡)、GSLB設備(Global Server Load Balance,全局負載均衡)等。

  • 任務分配器和業務伺服器的連接從簡單的“1對多”(1台任務分配器連接多台業務伺服器)變成了“多對多”(多台任務分配器連接多台業務伺服器)的網狀結構。

  • 機器數量從3台擴展到30台(一般任務分配器數量比業務伺服器要少,這裡我們假設業務伺服器為25台,任務分配器為5台),狀態管理、故障處理複雜度也大大增加。

上面這兩個例子都是以業務處理為例,實際上“任務”涵蓋的範圍很廣, 可以指完整的業務處理,也可以單指某個具體的任務。例如,“存儲”“運算”“緩存”等都可以作為一項任務,因此存儲系統、運算系統、緩存系統都可以按照任務分配的方式來搭建架構。此外,“任務分配器”也並不一定只能是物理上存在的機器或者一個獨立運行的程式,也可以是嵌入在其他程式中的演算法,例如Memcache的集群架構。

2.任務分解

通過任務分配的方式,我們能夠突破單台機器處理性能的瓶頸,通過增加更多的機器來滿足業務的性能需求,但如果業務本身也越來越複雜,單純只通過任務分配的方式來擴展性能,收益會越來越低。例如,業務簡單的時候1台機器擴展到10台機器,性能能夠提升8倍(需要扣除機器群帶來的部分性能損耗,因此無法達到理論上的10倍那麼高),但如果業務越來越複雜,1台機器擴展到10台,性能可能只能提升5倍。造成這種現象的主要原因是業務越來越複雜,單台機器處理的性能會越來越低。為了能夠繼續提升性能,我們需要採取第二種方式: 任務分解

繼續以上面“任務分配”中的架構為例,“業務伺服器”如果越來越複雜,我們可以將其拆分為更多的組成部分,我以微信的後臺架構為例。

通過上面的架構示意圖可以看出,微信後臺架構從邏輯上將各個子業務進行了拆分,包括:接入、註冊登錄、消息、LBS、搖一搖、漂流瓶、其他業務(聊天、視頻、朋友圈等)。

通過這種任務分解的方式,能夠把原來大一統但複雜的業務系統,拆分成小而簡單但需要多個系統配合的業務系統。從業務的角度來看,任務分解既不會減少功能,也不會減少代碼量(事實上代碼量可能還會增加,因為從代碼內部調用改為通過伺服器之間的介面調用),那為何通過任務分解就能夠提升性能呢?

主要有幾方面的因素:

  • 簡單的系統更加容易做到高性能

系統的功能越簡單,影響性能的點就越少,就更加容易進行有針對性的優化。而系統很複雜的情況下,首先是比較難以找到關鍵性能點,因為需要考慮和驗證的點太多;其次是即使花費很大力氣找到了,修改起來也不容易,因為可能將A關鍵性能點提升了,但卻無意中將B點的性能降低了,整個系統的性能不但沒有提升,還有可能會下降。

  • 可以針對單個任務進行擴展

當各個邏輯任務分解到獨立的子系統後,整個系統的性能瓶頸更加容易發現,而且發現後只需要針對有瓶頸的子系統進行性能優化或者提升,不需要改動整個系統,風險會小很多。以微信的後臺架構為例,如果用戶數增長太快,註冊登錄子系統性能出現瓶頸的時候,只需要優化登錄註冊子系統的性能(可以是代碼優化,也可以簡單粗暴地加機器),消息邏輯、LBS邏輯等其他子系統完全不需要改動。

既然將一個大一統的系統分解為多個子系統能夠提升性能,那是不是劃分得越細越好呢?例如,上面的微信後臺目前是7個邏輯子系統,如果我們把這7個邏輯子系統再細分,劃分為100個邏輯子系統,性能是不是會更高呢?

其實不然,這樣做性能不僅不會提升,反而還會下降,最主要的原因是如果系統拆分得太細,為了完成某個業務,系統間的調用次數會呈指數級別上升,而系統間的調用通道目前都是通過網路傳輸的方式,性能遠比系統內的函數調用要低得多。我以一個簡單的圖示來說明。

從圖中可見,當系統拆分為2個子系統時,用戶訪問需要1次系統間請求和1次響應;當系統拆分為4個子系統時,系統間請求次數從1次增加到3次;若繼續拆分為100個子系統,為完成某次用戶訪問,系統間請求次數將達到99次。

為簡化描述,我們抽象出一個最簡模型:假設這些系統通過IP網路連接,在理想情況下,一次請求和響應在網路上耗時為1ms,業務處理本身耗時為50ms。我們還假設系統拆分對單個業務請求性能沒有影響。因此,當系統拆分為2個子系統時,處理一次用戶訪問耗時為51ms;而系統拆分為100個子系統時,處理一次用戶訪問耗時竟然達到了149ms。

儘管在一定程度上,系統拆分有助於提升業務處理性能,但性能提升是有限的。當系統未拆分時,業務處理耗時為50ms,系統拆分後業務處理耗時不可能僅為1ms。因為業務處理性能仍然受限於業務邏輯本身,而在業務邏輯沒有發生重大變化的情況下,理論上性能具有一個上限。系統拆分可以讓性能接近這一極限,但無法突破它。因此,任務分解所帶來的性能收益具有一定的度,任務分解不是越細越好。對於架構設計而言,如何把握這一粒度便顯得至關重要。

小結

今天我向你講述了軟體系統中高性能帶來的複雜度主要體現的兩方面;

  • 一是單台電腦內部為了高性能帶來的複雜度;
  • 二是多台電腦集群為了高性能帶來的複雜度

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • > 內容摘自我的學習網站:topjavaer.cn ## 一面 - kafka在應用場景以及 項目 里的實現 - bitmap底層 - object里有哪些方法 - hashmap相關 - sychronized和reentrantlock相關問題以及鎖升級 - cas和volatile - 線程幾 ...
  • # Java基礎複習筆記 ## 第01章:Java語言概述 ### 1. Java基礎學習的章節劃分 ``` 第1階段:Java基本語法 Java語言概述、Java的變數與進位、運算符、流程式控制制語句(條件判斷、迴圈結構)、break\continue、 IDEA開發工具的使用、數組 第2階段:面向對 ...
  • 一、變數的聲明方式(三種) 1、var a int = num 2、var a = num 3、a := num 二、字元類型使用細節 *Golang的字元使用UTF-8. 英文 -1 位元組;漢字-3個位元組 1、字元常量用單引號括起來。 eg: var c1 byte='a' 2、在Go中,字元的本 ...
  • 見過好多大項目,一個解決方案好多個項目,網站、動態庫、測試等。放在不同的文件夾下,感覺很好。下麵介紹一下方法。 首先創建一個空白解決方案 會自動創建MultiFolder解決方案目錄。 ![img](https://img2023.cnblogs.com/blog/108012/202306/108 ...
  • 產品設計出來之後啊,大家使用的時候覺得反過來使用更加便捷。但是屏幕顯示是反的。那怎麼辦那????? 修改硬體費時費工,那能否軟體實現那????? 如果純軟體使用那就太費系統資源了。於是就想到了使用全志R528 自帶的G2D功能(硬體加速功能)。 使用它進行旋轉,後又發現uboot階段系統沒有G2D導 ...
  • 1. 背景 日誌領域是Elasticsearch(ES)最重要也是規模最大的應用場景之一。這得益於 ES 有高性能倒排索引、靈活的 schema、易用的分散式架構,支持高吞吐寫入、高性能查詢,同時有強大的數據治理生態、端到端的完整解決方案。但原生 ES 在高吞吐寫入、低成本存儲、高性能查詢等方面還有 ...
  • 上一篇文章 [我在 vscode 插件里接入了 ChatGPT,解決了代碼變數命名的難題](https://www.cnblogs.com/jaycewu/p/17476198.html) 中,展示瞭如何在 vscode 插件中使用 ChatGPT 解決代碼變數命名的問題。vscode 插件市場中有 ...
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...