資深架構(gòu)師手把手教你性能優(yōu)化
孔慶龍,一線架構(gòu)師,具有多年的金融架構(gòu)經(jīng)驗(yàn),具備 SOA 服務(wù)化、服務(wù)治理、系統(tǒng)優(yōu)化、分布式系統(tǒng)項(xiàng)目經(jīng)驗(yàn)。目前關(guān)注于互聯(lián)網(wǎng)金融技術(shù)架構(gòu)設(shè)計(jì)、分布式架構(gòu)、微服務(wù)架構(gòu)、DevOps 實(shí)踐、大數(shù)據(jù)等領(lǐng)域。
1. 什么是系統(tǒng)優(yōu)化
系統(tǒng)優(yōu)化,一方面是系統(tǒng)化地對 IT 系統(tǒng)或交易鏈上的每個(gè)環(huán)節(jié)進(jìn)行分析并優(yōu)化,另一方面是對單一系統(tǒng)進(jìn)行瓶頸點(diǎn)分析和優(yōu)化 。這兩方面的優(yōu)化目標(biāo)大致相同,無非是提高系統(tǒng)的響應(yīng)速度、吞吐量、降低各層耦合,以靈活應(yīng)對市場需要。系統(tǒng)優(yōu)化主要在如下三個(gè)層面上進(jìn)行。
-
IT 系統(tǒng)治理層: 這一層面的優(yōu)化不只是性能優(yōu)化,還包括為適應(yīng)業(yè)務(wù)架構(gòu)變化而帶來的應(yīng)用架構(gòu)優(yōu)化(如應(yīng)用分層、服務(wù)治理等)。
-
系統(tǒng)層: 這一層面的優(yōu)化包括業(yè)務(wù)流程優(yōu)化、數(shù)據(jù)流程優(yōu)化(如提高系統(tǒng)負(fù)載、減少系統(tǒng)開銷等)。
-
基礎(chǔ)設(shè)施層: 這一層面的優(yōu)化主要是提高 IaaS 平臺的能力(如使彈性集群具備橫向擴(kuò)展能力、支持資源快速上下線和轉(zhuǎn)移等)。
2. 系統(tǒng)優(yōu)化的方法論、思路和原則
什么是方法論,我個(gè)人的理解就是聽起來很厲害,做過的人認(rèn)為是廢話,但可以指明行動方向的理論。 下面就根據(jù)多年經(jīng)驗(yàn),從系統(tǒng)優(yōu)化的 常用方法論 、 優(yōu)化思路 和 優(yōu)化原則 三方面進(jìn)行簡單分享。
2.1 常用方法論
常用的方法論有以下幾條。
-
阿姆達(dá)爾定律(Amdahl Law): 根據(jù)公式 S=1/(1−a+a/n)分析每次對整體效果影響最大的點(diǎn),并進(jìn)行優(yōu)化。
-
不訪問不必要的數(shù)據(jù): 減少交易線上的不必要環(huán)節(jié),減少故障點(diǎn)和維護(hù)點(diǎn)。
-
就近加載,緩存為王。
-
故障隔離: 不要因?yàn)橐粋€(gè)系統(tǒng)瓶頸壓垮整個(gè)交易平臺。
-
具備良好的擴(kuò)展能力: 合理地利用資源,提高處理效率并避免單點(diǎn)故障。
-
對交易鏈進(jìn)行優(yōu)化,提高吞吐量:減少串行同步調(diào)用,合理拆分(垂直/水平拆分),規(guī)則前置。
-
性能和功能同等重要: 若交易鏈上有 5 個(gè)性能變?yōu)樵O(shè)計(jì)階段時(shí)的 90%,則整體性能會變?yōu)樵O(shè)計(jì)時(shí)的 59%。
2.2 優(yōu)化思路
根據(jù)以往經(jīng)驗(yàn)總結(jié)出系統(tǒng)優(yōu)化的思路,大致如下:
(1)了解現(xiàn)狀,發(fā)現(xiàn)問題。
(2)確定清晰的優(yōu)化目標(biāo),分析現(xiàn)狀與目標(biāo)的差距并確定執(zhí)行路線。
(3)對系統(tǒng)進(jìn)行拆分,分別對邏輯層(Web 層、業(yè)務(wù)層、持久化層)和物理層(客戶端、網(wǎng)絡(luò)、應(yīng)用服務(wù)器、數(shù)據(jù)庫服務(wù)器)進(jìn)行優(yōu)化。
(4)利用工具對系統(tǒng)進(jìn)行監(jiān)控和測試,并對監(jiān)控和測試結(jié)果進(jìn)行分析。
(5)科學(xué)地對系統(tǒng)進(jìn)行優(yōu)化。
-
遵循一定的程序: 監(jiān)控/性能測試→分析瓶頸→羅列瓶頸的因素→驗(yàn)證瓶頸因素 →修改系統(tǒng)→確認(rèn)是否達(dá)到優(yōu)化目標(biāo)。
-
確定影響性能的因素: CPU、內(nèi)存、I/O、網(wǎng)絡(luò)或其他因素。
-
找出主要的瓶頸,優(yōu)先解決關(guān)鍵因素,再重復(fù)監(jiān)控或測試驗(yàn)證。
-
避免過度優(yōu)化,一次修改一個(gè)瓶頸,不要對不需要的地方進(jìn)行優(yōu)化。
-
提高 CPU 性能: 寫出更快的代碼,設(shè)計(jì)出更好的算法,減少短期生存的對象。
-
提高內(nèi)存性能: 減少長期生存的對象。
-
提高 I/O 性能: 重新設(shè)計(jì)應(yīng)用,減少 I/O 的交互。
-
緩存為王: 適度緩存,做到最大化發(fā)揮數(shù)據(jù)庫緩存、應(yīng)用端緩存、客戶端緩存的作用。
2.3 優(yōu)化原則
系統(tǒng)優(yōu)化的原則如下:
-
在應(yīng)用系統(tǒng)的設(shè)計(jì)、開發(fā)過程中,應(yīng)當(dāng)始終把性能放在考慮的范圍內(nèi)。
-
確定清晰明確的性能目標(biāo)是關(guān)鍵。
-
性能優(yōu)化是伴隨整個(gè)項(xiàng)目周期的,最好分階段設(shè)定目標(biāo),在達(dá)到預(yù)期性能目標(biāo)之后,即可對本階段工作進(jìn)行總結(jié)和知識轉(zhuǎn)移,進(jìn)入下一階段的優(yōu)化工作。
-
必須保證優(yōu)化后的程序可以正確運(yùn)行。
-
性能更大程度上取決于良好的設(shè)計(jì),優(yōu)化技巧只是一個(gè)輔助手段。
-
優(yōu)化過程是迭代漸進(jìn)的過程,每次優(yōu)化的結(jié)果都要反饋到后續(xù)的代碼開發(fā)中。
-
性能優(yōu)化不能以犧牲代碼的可讀性和維護(hù)性為代價(jià)。
3. 性能優(yōu)化
3.1 常見的性能問題
常見的性能問題大多數(shù)是由于資源不足或熱點(diǎn)資源競爭導(dǎo)致的,下面將從客戶端性能、 J2EE 系統(tǒng)性能和數(shù)據(jù)庫性能三方面進(jìn)行簡單介紹。
常見的客戶端性能問題
常見的客戶端性能問題有如下幾項(xiàng)。
-
加載慢: 第一次啟動慢或者重新加載慢。
-
無響應(yīng): 事件突發(fā)導(dǎo)致頁面假死。
-
受網(wǎng)絡(luò)帶寬影響嚴(yán)重: 因?yàn)樾枰螺d大量資源文件,所以在一些網(wǎng)絡(luò)環(huán)境不好的地區(qū)頁面加載緩慢。
-
JS(JavaScript)內(nèi)存溢出: 頻繁地對對象屬性進(jìn)行操作,造成內(nèi)存被大量占用,最終溢出。
常見的 J2EE 系統(tǒng)性能問題
常見的 J2EE 系統(tǒng)性能問題有如下幾項(xiàng)。
-
內(nèi)存泄漏:在運(yùn)行過程中,內(nèi)存不斷被占用而不能被回收,內(nèi)存使用率隨著時(shí)間的推移或負(fù)載的增加呈線性增長,系統(tǒng)處理效率隨著時(shí)間的推移或并發(fā)的增加而下降,直至將分配給 JVM 的內(nèi)存用盡。
-
資源泄漏:將資源打開后未關(guān)閉或未成功關(guān)閉而導(dǎo)致的問題。這些資源包括數(shù)據(jù)源連接、文件流等。當(dāng)這些資源經(jīng)常被打開而未能成功關(guān)閉時(shí),就會導(dǎo)致資源泄漏。數(shù)據(jù)源連接泄漏是常見的資源泄漏問題。
-
過載:過度使用系統(tǒng),超出其所能承受的負(fù)荷。
-
資源瓶頸:資源被過度使用或分配不足而引起資源瓶頸問題。
-
線程阻塞、線程死鎖:線程執(zhí)行某段代碼時(shí),無法獲得相關(guān)的同步鎖而造成通信阻塞。
-
應(yīng)用系統(tǒng)響應(yīng)慢:由于應(yīng)用算法未優(yōu)化,不合理地讀取大量數(shù)據(jù),SQL 缺少索引或存在過多表關(guān)聯(lián)而導(dǎo)致響應(yīng)時(shí)間過長,執(zhí)行變慢。
-
應(yīng)用系統(tǒng)不穩(wěn)定:應(yīng)用系統(tǒng)的響應(yīng)出現(xiàn)時(shí)快時(shí)慢的現(xiàn)象。
-
應(yīng)用系統(tǒng)中有各種各樣的異常情況發(fā)生:有些是中間件服務(wù)器拋出的異常,有些是數(shù)據(jù)端拋出的異常。
常見的數(shù)據(jù)庫問題
在以往做核心業(yè)務(wù)系統(tǒng)技術(shù)支持時(shí),遇到的常見的數(shù)據(jù)庫問題如下所述。
-
死鎖:由于聯(lián)機(jī)服務(wù)過早開啟數(shù)據(jù)庫事務(wù),第三方服務(wù)未及時(shí)響應(yīng),使得鎖和事務(wù)無法提交而導(dǎo)致數(shù)據(jù)庫死鎖。
-
I/O 繁忙:由于存在不良 SQL 或業(yè)務(wù)邏輯設(shè)計(jì)不合理而導(dǎo)致大量 I/O 等待。
-
CPU 使用率居高不下:由于高并發(fā)或緩存穿透而導(dǎo)致數(shù)據(jù)庫 CPU 居高不下或忽高忽低。
3.2 性能優(yōu)化的具體工作
“天下武功,唯快不破”,性能優(yōu)化的首要工作就是提高系統(tǒng)的響應(yīng)時(shí)間(響應(yīng)時(shí)間 = 服務(wù)處理時(shí)間 + 排隊(duì)時(shí)間)。如圖 16.1 所示的經(jīng)典響應(yīng)時(shí)間曲線,我們要做的就是通過優(yōu)化程序來減少服務(wù)時(shí)間,通過提高系統(tǒng)的吞吐量減少系統(tǒng)的排隊(duì)時(shí)間。圖 16.1 中的縱軸代表的是響應(yīng)時(shí)間,即服務(wù)時(shí)間和排隊(duì)時(shí)間的總和; 橫軸代表的是到達(dá)率。隨著每單位時(shí)間進(jìn)入系統(tǒng)的事務(wù)數(shù)的增加,曲線逐漸向右滑動。隨著到達(dá)率的增加,排隊(duì)時(shí)間會在某一時(shí)刻陡然上升,此時(shí),響應(yīng)時(shí)間也將陡然上升,性能下降,而用戶會感到非常沮喪。
圖 16.1
下面通過以往項(xiàng)目中的案例來分析性能優(yōu)化的具體工作。
交易線優(yōu)化
交易線用來從服務(wù)消費(fèi)者的角度查看交易在各個(gè)層面上應(yīng)該完成的功能,以及功能點(diǎn)之間的關(guān)系。功能點(diǎn)之間的關(guān)系用有向路徑來表示,如圖 16.2 所示。
圖 16.2
交易線優(yōu)化的原則如下:
-
最短路徑: 減少不必要的環(huán)節(jié),避免故障點(diǎn)。
-
交易完整性: 通過沖正或補(bǔ)償交易等確保交易線各環(huán)節(jié)的事物一致性。
-
故障隔離和快速定位: 屏蔽異常情況對正常交易的影響,通過交易碼或錯(cuò)誤碼快速定位問題。
-
流量控制原則: 可以對服務(wù)通道進(jìn)行流量控制,并結(jié)合優(yōu)先級設(shè)置優(yōu)先處理級別高的業(yè)務(wù)。
-
超時(shí)控制漏斗原則: 盡量使交易線上前端系統(tǒng)的超時(shí)設(shè)置大于后端系統(tǒng)。
-
熔斷和故障隔離原則: 避免由于某個(gè)服務(wù)提供者出現(xiàn)故障而導(dǎo)致整個(gè)交易線不可用。
隨著架構(gòu)的演變,現(xiàn)在已經(jīng)由豎井式系統(tǒng)逐步發(fā)展為以服務(wù)為單元、可靈活構(gòu)建的分布式服務(wù)體系,如圖 16.3 所示。在服務(wù)治理的過程中,原來的核心業(yè)務(wù)系統(tǒng)被打碎為各種獨(dú)立的業(yè)務(wù)組件,一些中間層平臺型系統(tǒng)基于這些業(yè)務(wù)組件和流程服務(wù)逐漸構(gòu)建了業(yè)務(wù)服務(wù),并為前端應(yīng)用的快速構(gòu)建提供業(yè)務(wù)支撐。在這個(gè)過程中,服務(wù)識別和構(gòu)建是基礎(chǔ),交易線的規(guī)范是保障,通過交易線規(guī)范可以確定服務(wù)治理的涉及范圍,因?yàn)樵谲浖姹镜鷷r(shí),很少有人能把系統(tǒng)的全部細(xì)節(jié)都考慮清楚,所以要靠規(guī)范來確定治理范圍,而不是靠人。
圖 16.3
要開發(fā)一個(gè)訂單查詢功能 A,服務(wù)整合平臺的 B 和 C 兩個(gè)服務(wù)都可以完成此功能的開發(fā),不同的是 B 在 C 的基礎(chǔ)上增加了一些額外的校驗(yàn)。按照最短路徑原則,A 應(yīng)該直接調(diào) 用 C 服務(wù),如圖 16.4 所示。
圖 16.4
流量控制的目的之一是保證各系統(tǒng)健康穩(wěn)定地運(yùn)行。一般使用計(jì)數(shù)器按照交易類型來檢測交易的并發(fā)數(shù),不同交易類型使用不同的計(jì)數(shù)器。當(dāng)交易請求到達(dá)時(shí),計(jì)數(shù)器加 1; 當(dāng)請求響應(yīng)失敗時(shí),計(jì)數(shù)器減 1。
流量控制是對服務(wù)提供者的一種保護(hù)機(jī)制,那么服務(wù)消費(fèi)者如何避免因?yàn)榉?wù)提供者不可用而導(dǎo)致自身不可用,并逐級向交易鏈的調(diào)用方放大這種不可用,最終拖垮整個(gè)交易鏈路導(dǎo)致雪崩的情況呢? 服務(wù)的消費(fèi)方一般可以通過以下 3 種方式來防止“雪崩”,實(shí)現(xiàn)對交易鏈路的保護(hù)。
隔離機(jī)制:資源池隔離。如果將線程池、數(shù)據(jù)庫連接池等獨(dú)立分配,那么即使某類 服務(wù)出現(xiàn)問題也只會影響單獨(dú)的資源池。
-
熔斷機(jī)制: 當(dāng)調(diào)用失敗,觸發(fā)預(yù)設(shè)的閾值時(shí),應(yīng)快速返回預(yù)設(shè)結(jié)果,避免大量的同步等待,熔斷偵測請求會定期檢測服務(wù)狀態(tài),進(jìn)行服務(wù)狀態(tài)轉(zhuǎn)換。
-
監(jiān)控報(bào)警: 對服務(wù)調(diào)用狀態(tài)進(jìn)行監(jiān)控并設(shè)置預(yù)警值,在發(fā)生異常時(shí)可以及時(shí)通知相關(guān)人員進(jìn)行干預(yù)處理,縮短異常影響范圍。
客戶端優(yōu)化
圖 16. 5
從 Web 請求時(shí)序(如圖 16.5 所示)中可以看出,客戶端優(yōu)化的首要目標(biāo)是加快頁面展 現(xiàn)和響應(yīng)速度,其次是減少對服務(wù)器端的調(diào)用,常見的解決辦法如下:
-
分析瓶頸點(diǎn),有針對性地進(jìn)行優(yōu)化。
-
緩存為王,通過在客戶端緩存靜態(tài)數(shù)據(jù)提升頁面響應(yīng)時(shí)間。
-
通過 gzip、deflate 壓縮來減少客戶端網(wǎng)絡(luò)的下載流量。執(zhí)行壓縮的好處是可以減少 60%左右的文本類文件所占用的空間,但執(zhí)行時(shí)需要注意解析 HTTP 請求的, Accept-Encoding 頭判斷是否支持壓縮,并在響應(yīng)中設(shè)置 Content-Encoding 編碼格式。
-
使用壓縮工具對 JS 進(jìn)行壓縮,減小 JS 文件所占用的空間。
-
刪除、合并腳本、樣式表及圖片,減少 GET 請求。*無阻塞加載 JS。
-
預(yù)加載圖片、CSS 樣式、JS 腳本。
-
按需加載 JS 腳本。
-
優(yōu)化 JS 處理方法,提升頁面處理速度。
圖 16.6 所示的是某企業(yè)內(nèi)部應(yīng)用系統(tǒng)客戶端的 HTTP 請求監(jiān)控記錄。
圖 16.6
從圖 16.6 中可以看到共計(jì)發(fā)送 25 次請求(21 次命中緩存、4 次與服務(wù)器端交互)。從 圖 16.7 所示的統(tǒng)計(jì)信息中可以看到: 請求耗時(shí)總計(jì) 5.645s,進(jìn)行了 4 次網(wǎng)絡(luò)交互,接收到 5.9KB 數(shù)據(jù),發(fā)送了 110.25KB 數(shù)據(jù),使用 gzip 壓縮節(jié)省了 8KB 數(shù)據(jù)。
圖 16.7
通過優(yōu)化后端請求、合并和壓縮 JS/JSP 文件等操作,將頁面響應(yīng)時(shí)間優(yōu)化到 2s 左右。
進(jìn)行前端優(yōu)化最好了解瀏覽器原理、HTTP 原理。
服務(wù)器端優(yōu)化
服務(wù)器端的涉及面比較廣,圖 16.8 整理了在進(jìn)行服務(wù)器端優(yōu)化時(shí)可能存在的問題,以及所采用的輔助分析工具、分析思路、常見解決辦法。
圖 16.8