資深架構師手把手教你性能優化
孔慶龍,一線架構師,具有多年的金融架構經驗,具備 SOA 服務化、服務治理、系統優化、分布式系統項目經驗。目前關注于互聯網金融技術架構設計、分布式架構、微服務架構、DevOps 實踐、大數據等領域。
1. 什么是系統優化
系統優化,一方面是系統化地對 IT 系統或交易鏈上的每個環節進行分析并優化,另一方面是對單一系統進行瓶頸點分析和優化 。這兩方面的優化目標大致相同,無非是提高系統的響應速度、吞吐量、降低各層耦合,以靈活應對市場需要。系統優化主要在如下三個層面上進行。
-
IT 系統治理層: 這一層面的優化不只是性能優化,還包括為適應業務架構變化而帶來的應用架構優化(如應用分層、服務治理等)。
-
系統層: 這一層面的優化包括業務流程優化、數據流程優化(如提高系統負載、減少系統開銷等)。
-
基礎設施層: 這一層面的優化主要是提高 IaaS 平臺的能力(如使彈性集群具備橫向擴展能力、支持資源快速上下線和轉移等)。
2. 系統優化的方法論、思路和原則
什么是方法論,我個人的理解就是聽起來很厲害,做過的人認為是廢話,但可以指明行動方向的理論。 下面就根據多年經驗,從系統優化的 常用方法論 、 優化思路 和 優化原則 三方面進行簡單分享。
2.1 常用方法論
常用的方法論有以下幾條。
-
阿姆達爾定律(Amdahl Law): 根據公式 S=1/(1−a+a/n)分析每次對整體效果影響最大的點,并進行優化。
-
不訪問不必要的數據: 減少交易線上的不必要環節,減少故障點和維護點。
-
就近加載,緩存為王。
-
故障隔離: 不要因為一個系統瓶頸壓垮整個交易平臺。
-
具備良好的擴展能力: 合理地利用資源,提高處理效率并避免單點故障。
-
對交易鏈進行優化,提高吞吐量:減少串行同步調用,合理拆分(垂直/水平拆分),規則前置。
-
性能和功能同等重要: 若交易鏈上有 5 個性能變為設計階段時的 90%,則整體性能會變為設計時的 59%。
2.2 優化思路
根據以往經驗總結出系統優化的思路,大致如下:
(1)了解現狀,發現問題。
(2)確定清晰的優化目標,分析現狀與目標的差距并確定執行路線。
(3)對系統進行拆分,分別對邏輯層(Web 層、業務層、持久化層)和物理層(客戶端、網絡、應用服務器、數據庫服務器)進行優化。
(4)利用工具對系統進行監控和測試,并對監控和測試結果進行分析。
(5)科學地對系統進行優化。
-
遵循一定的程序: 監控/性能測試→分析瓶頸→羅列瓶頸的因素→驗證瓶頸因素 →修改系統→確認是否達到優化目標。
-
確定影響性能的因素: CPU、內存、I/O、網絡或其他因素。
-
找出主要的瓶頸,優先解決關鍵因素,再重復監控或測試驗證。
-
避免過度優化,一次修改一個瓶頸,不要對不需要的地方進行優化。
-
提高 CPU 性能: 寫出更快的代碼,設計出更好的算法,減少短期生存的對象。
-
提高內存性能: 減少長期生存的對象。
-
提高 I/O 性能: 重新設計應用,減少 I/O 的交互。
-
緩存為王: 適度緩存,做到最大化發揮數據庫緩存、應用端緩存、客戶端緩存的作用。
2.3 優化原則
系統優化的原則如下:
-
在應用系統的設計、開發過程中,應當始終把性能放在考慮的范圍內。
-
確定清晰明確的性能目標是關鍵。
-
性能優化是伴隨整個項目周期的,最好分階段設定目標,在達到預期性能目標之后,即可對本階段工作進行總結和知識轉移,進入下一階段的優化工作。
-
必須保證優化后的程序可以正確運行。
-
性能更大程度上取決于良好的設計,優化技巧只是一個輔助手段。
-
優化過程是迭代漸進的過程,每次優化的結果都要反饋到后續的代碼開發中。
-
性能優化不能以犧牲代碼的可讀性和維護性為代價。
3. 性能優化
3.1 常見的性能問題
常見的性能問題大多數是由于資源不足或熱點資源競爭導致的,下面將從客戶端性能、 J2EE 系統性能和數據庫性能三方面進行簡單介紹。
常見的客戶端性能問題
常見的客戶端性能問題有如下幾項。
-
加載慢: 第一次啟動慢或者重新加載慢。
-
無響應: 事件突發導致頁面假死。
-
受網絡帶寬影響嚴重: 因為需要下載大量資源文件,所以在一些網絡環境不好的地區頁面加載緩慢。
-
JS(JavaScript)內存溢出: 頻繁地對對象屬性進行操作,造成內存被大量占用,最終溢出。
常見的 J2EE 系統性能問題
常見的 J2EE 系統性能問題有如下幾項。
-
內存泄漏:在運行過程中,內存不斷被占用而不能被回收,內存使用率隨著時間的推移或負載的增加呈線性增長,系統處理效率隨著時間的推移或并發的增加而下降,直至將分配給 JVM 的內存用盡。
-
資源泄漏:將資源打開后未關閉或未成功關閉而導致的問題。這些資源包括數據源連接、文件流等。當這些資源經常被打開而未能成功關閉時,就會導致資源泄漏。數據源連接泄漏是常見的資源泄漏問題。
-
過載:過度使用系統,超出其所能承受的負荷。
-
資源瓶頸:資源被過度使用或分配不足而引起資源瓶頸問題。
-
線程阻塞、線程死鎖:線程執行某段代碼時,無法獲得相關的同步鎖而造成通信阻塞。
-
應用系統響應慢:由于應用算法未優化,不合理地讀取大量數據,SQL 缺少索引或存在過多表關聯而導致響應時間過長,執行變慢。
-
應用系統不穩定:應用系統的響應出現時快時慢的現象。
-
應用系統中有各種各樣的異常情況發生:有些是中間件服務器拋出的異常,有些是數據端拋出的異常。
常見的數據庫問題
在以往做核心業務系統技術支持時,遇到的常見的數據庫問題如下所述。
-
死鎖:由于聯機服務過早開啟數據庫事務,第三方服務未及時響應,使得鎖和事務無法提交而導致數據庫死鎖。
-
I/O 繁忙:由于存在不良 SQL 或業務邏輯設計不合理而導致大量 I/O 等待。
-
CPU 使用率居高不下:由于高并發或緩存穿透而導致數據庫 CPU 居高不下或忽高忽低。
3.2 性能優化的具體工作
“天下武功,唯快不破”,性能優化的首要工作就是提高系統的響應時間(響應時間 = 服務處理時間 + 排隊時間)。如圖 16.1 所示的經典響應時間曲線,我們要做的就是通過優化程序來減少服務時間,通過提高系統的吞吐量減少系統的排隊時間。圖 16.1 中的縱軸代表的是響應時間,即服務時間和排隊時間的總和; 橫軸代表的是到達率。隨著每單位時間進入系統的事務數的增加,曲線逐漸向右滑動。隨著到達率的增加,排隊時間會在某一時刻陡然上升,此時,響應時間也將陡然上升,性能下降,而用戶會感到非常沮喪。

圖 16.1
下面通過以往項目中的案例來分析性能優化的具體工作。
交易線優化
交易線用來從服務消費者的角度查看交易在各個層面上應該完成的功能,以及功能點之間的關系。功能點之間的關系用有向路徑來表示,如圖 16.2 所示。

圖 16.2
交易線優化的原則如下:
-
最短路徑: 減少不必要的環節,避免故障點。
-
交易完整性: 通過沖正或補償交易等確保交易線各環節的事物一致性。
-
故障隔離和快速定位: 屏蔽異常情況對正常交易的影響,通過交易碼或錯誤碼快速定位問題。
-
流量控制原則: 可以對服務通道進行流量控制,并結合優先級設置優先處理級別高的業務。
-
超時控制漏斗原則: 盡量使交易線上前端系統的超時設置大于后端系統。
-
熔斷和故障隔離原則: 避免由于某個服務提供者出現故障而導致整個交易線不可用。
隨著架構的演變,現在已經由豎井式系統逐步發展為以服務為單元、可靈活構建的分布式服務體系,如圖 16.3 所示。在服務治理的過程中,原來的核心業務系統被打碎為各種獨立的業務組件,一些中間層平臺型系統基于這些業務組件和流程服務逐漸構建了業務服務,并為前端應用的快速構建提供業務支撐。在這個過程中,服務識別和構建是基礎,交易線的規范是保障,通過交易線規范可以確定服務治理的涉及范圍,因為在軟件版本迭代時,很少有人能把系統的全部細節都考慮清楚,所以要靠規范來確定治理范圍,而不是靠人。

圖 16.3
要開發一個訂單查詢功能 A,服務整合平臺的 B 和 C 兩個服務都可以完成此功能的開發,不同的是 B 在 C 的基礎上增加了一些額外的校驗。按照最短路徑原則,A 應該直接調 用 C 服務,如圖 16.4 所示。

圖 16.4
流量控制的目的之一是保證各系統健康穩定地運行。一般使用計數器按照交易類型來檢測交易的并發數,不同交易類型使用不同的計數器。當交易請求到達時,計數器加 1; 當請求響應失敗時,計數器減 1。
流量控制是對服務提供者的一種保護機制,那么服務消費者如何避免因為服務提供者不可用而導致自身不可用,并逐級向交易鏈的調用方放大這種不可用,最終拖垮整個交易鏈路導致雪崩的情況呢? 服務的消費方一般可以通過以下 3 種方式來防止“雪崩”,實現對交易鏈路的保護。
隔離機制:資源池隔離。如果將線程池、數據庫連接池等獨立分配,那么即使某類 服務出現問題也只會影響單獨的資源池。
-
熔斷機制: 當調用失敗,觸發預設的閾值時,應快速返回預設結果,避免大量的同步等待,熔斷偵測請求會定期檢測服務狀態,進行服務狀態轉換。
-
監控報警: 對服務調用狀態進行監控并設置預警值,在發生異常時可以及時通知相關人員進行干預處理,縮短異常影響范圍。
客戶端優化

圖 16. 5
從 Web 請求時序(如圖 16.5 所示)中可以看出,客戶端優化的首要目標是加快頁面展 現和響應速度,其次是減少對服務器端的調用,常見的解決辦法如下:
-
分析瓶頸點,有針對性地進行優化。
-
緩存為王,通過在客戶端緩存靜態數據提升頁面響應時間。
-
通過 gzip、deflate 壓縮來減少客戶端網絡的下載流量。執行壓縮的好處是可以減少 60%左右的文本類文件所占用的空間,但執行時需要注意解析 HTTP 請求的, Accept-Encoding 頭判斷是否支持壓縮,并在響應中設置 Content-Encoding 編碼格式。
-
使用壓縮工具對 JS 進行壓縮,減小 JS 文件所占用的空間。
-
刪除、合并腳本、樣式表及圖片,減少 GET 請求。*無阻塞加載 JS。
-
預加載圖片、CSS 樣式、JS 腳本。
-
按需加載 JS 腳本。
-
優化 JS 處理方法,提升頁面處理速度。
圖 16.6 所示的是某企業內部應用系統客戶端的 HTTP 請求監控記錄。

圖 16.6
從圖 16.6 中可以看到共計發送 25 次請求(21 次命中緩存、4 次與服務器端交互)。從 圖 16.7 所示的統計信息中可以看到: 請求耗時總計 5.645s,進行了 4 次網絡交互,接收到 5.9KB 數據,發送了 110.25KB 數據,使用 gzip 壓縮節省了 8KB 數據。

圖 16.7
通過優化后端請求、合并和壓縮 JS/JSP 文件等操作,將頁面響應時間優化到 2s 左右。
進行前端優化最好了解瀏覽器原理、HTTP 原理。
服務器端優化
服務器端的涉及面比較廣,圖 16.8 整理了在進行服務器端優化時可能存在的問題,以及所采用的輔助分析工具、分析思路、常見解決辦法。

圖 16.8



























