也许90%的人都没有真正搞懂性能优化
作為一個(gè)半吊子全棧工匠,在20多年的職業(yè)生涯里遇到過(guò)太多關(guān)于軟件性能的問(wèn)題。論證或者證明性能的問(wèn)題往往很關(guān)鍵,能否通過(guò)一次一個(gè)小而有邏輯的可證明可審核的步驟來(lái)解決性能問(wèn)題呢?
曾經(jīng)企圖創(chuàng)建一種公理化的方法來(lái)優(yōu)化計(jì)算機(jī)軟件性能,然而能力所限,慚愧之至。退而求其次,希望能夠清楚地系統(tǒng)思考如何優(yōu)化計(jì)算機(jī)軟件的性能。
一、什么是性能?明確概念
性能——performance,有著太多概念外延,在生活中幾乎隨時(shí)可見,例如,職場(chǎng)人的performance就是中文里的績(jī)效,performance review 就是每人都會(huì)面對(duì)的績(jī)效考核。但是,如果在互聯(lián)網(wǎng)上百度一下,大多數(shù)有關(guān)性能的熱門文章是關(guān)于: 計(jì)算機(jī)軟件執(zhí)行任何您指定任務(wù)所需的時(shí)間。
如果把面向?qū)ο笞鳛殚_始,那什么是任務(wù)呢?任務(wù),基本上是一個(gè)面向業(yè)務(wù)的工作單元,任務(wù)可以嵌套。對(duì)于計(jì)算機(jī)用戶來(lái)說(shuō),性能通常意味著系統(tǒng)執(zhí)行某項(xiàng)任務(wù)所需的時(shí)間。響應(yīng)時(shí)間是任務(wù)的執(zhí)行持續(xù)時(shí)間,以每個(gè)任務(wù)的時(shí)間為單位,例如,在百度上搜索“性能” 的響應(yīng)時(shí)間為0.2秒左右,在瀏覽器中可以有辦法看到這個(gè)測(cè)量結(jié)果,這就是網(wǎng)頁(yè)搜索的一個(gè)性能證據(jù)。
由于感受軟件性能的主體是人,不同的人對(duì)于同樣的軟件能有不同的主觀感受,而且不同的人對(duì)于軟件性能關(guān)心的視角也不同。有些人眼中的性能是吞吐量,即在指定時(shí)間間隔內(nèi)完成的任務(wù)執(zhí)行數(shù)量,例如“每秒點(diǎn)擊次數(shù)” 。一般來(lái)說(shuō),負(fù)責(zé)團(tuán)隊(duì)性能的人更擔(dān)心吞吐量,因?yàn)樗麄円P(guān)心該系統(tǒng)是否能夠處理所有用戶需要要處理的所有數(shù)據(jù)。
那什么是性能呢?時(shí)空可能是連續(xù)的,從時(shí)空的視角看,性能是完成某項(xiàng)任務(wù)時(shí)所展示出來(lái)的時(shí)間及時(shí)性和空間資源有效性。對(duì)用戶而言,更關(guān)注及時(shí)性,對(duì)服務(wù)或者產(chǎn)品提供者而言,既關(guān)注時(shí)間又關(guān)注空間,是多種因素的權(quán)衡。
二、性能指標(biāo)——時(shí)空糾纏
性能的指標(biāo),是指衡量性能的尺度。從時(shí)間的維度看,包括響應(yīng)時(shí)間、延遲時(shí)間等,從空間的維度看,包括吞吐量,并發(fā)用戶數(shù)和資源利用率等。
由于時(shí)空的內(nèi)在聯(lián)系,以兩個(gè)重要的指標(biāo)為例,吞吐量和響應(yīng)時(shí)間通常相互關(guān)聯(lián),但并不完全相同,真正的關(guān)系是微妙而復(fù)雜的。
通信中的吞吐量與響應(yīng)時(shí)間
假設(shè)為某個(gè)基準(zhǔn)測(cè)試以每秒1000個(gè)任務(wù)的速度度量了吞吐量。那么,用戶的平均響應(yīng)時(shí)間是多少呢?人們很容易認(rèn)為每個(gè)任務(wù)的平均響應(yīng)時(shí)間是0.001秒,但事實(shí)并非如此。如果處理這個(gè)吞吐量的系統(tǒng)是有1000個(gè)并行的、獨(dú)立的、同質(zhì)的服務(wù)通道,在這種情況下,每個(gè)請(qǐng)求可能正好消耗1秒。
現(xiàn)在,可以知道每個(gè)任務(wù)的平均響應(yīng)時(shí)間在0到1秒之間。然而,不能僅僅從吞吐量測(cè)量中推導(dǎo)出響應(yīng)時(shí)間,必須單獨(dú)測(cè)量它。當(dāng)然,有數(shù)學(xué)模型可以計(jì)算給定吞吐量的響應(yīng)時(shí)間,但是模型需要更多的輸入,而不僅僅是吞吐量。
計(jì)算中的吞吐量與響應(yīng)時(shí)間
在另一個(gè)方向上,展露了微妙之處。如果需要在單CPU計(jì)算機(jī)上編程以提供每秒100個(gè)新任務(wù)的吞吐量,假設(shè)編寫的新任務(wù)在計(jì)算機(jī)系統(tǒng)上執(zhí)行僅用0.001秒,那么是否能產(chǎn)生所需的吞吐量?如果能在千分之一秒內(nèi)運(yùn)行一次任務(wù),那么肯定能在一整秒內(nèi)至少運(yùn)行100次。例如,任務(wù)請(qǐng)求被很好地序列化,就可以在一個(gè)循環(huán)中處理所有100個(gè)任務(wù),一個(gè)接著一個(gè)地循序執(zhí)行。
但是,如果每秒100個(gè)任務(wù)隨機(jī)地出現(xiàn)在系統(tǒng)上,從100個(gè)不同的用戶登錄到單 CPU 計(jì)算機(jī)上,又會(huì)怎樣呢?CPU 調(diào)度器和序列化資源可能會(huì)將吞吐量限制在遠(yuǎn)低于每秒100個(gè)的任務(wù)數(shù)量,從而不能完全從響應(yīng)時(shí)間度量推導(dǎo)出吞吐量,需要單獨(dú)測(cè)量。
響應(yīng)時(shí)間和吞吐量不一定是相反的。要了解這兩者,需要同時(shí)測(cè)量它們。哪一個(gè)更重要呢?對(duì)于給定的情況,可以從兩個(gè)方向上合理地尋找答案。在許多情況下,答案是兩者都是需要管理的重要指標(biāo)。例如,系統(tǒng)可能有一個(gè)業(yè)務(wù)需求,不僅要求在99%以上的系統(tǒng)響應(yīng)中,對(duì)給定任務(wù)的響應(yīng)時(shí)間必須小于1秒,而且系統(tǒng)必須支持在1秒間隔內(nèi)持續(xù)執(zhí)行1,000個(gè)任務(wù)的吞吐量。
三、描述性能:一切結(jié)果,都是概率
“在99%以上的系統(tǒng)響應(yīng)”,是一種響應(yīng)時(shí)間的期望限定,一些人更習(xí)慣于用“平均響應(yīng)時(shí)間必須是 x 秒”來(lái)描述。不過(guò),說(shuō)明目標(biāo)的百分比方法更好地體現(xiàn)在人們經(jīng)驗(yàn)中。
想象一下,對(duì)于每天在電腦上執(zhí)行的某項(xiàng)任務(wù),響應(yīng)時(shí)間容忍度可能是1秒。假設(shè),a系統(tǒng)90% 的平均響應(yīng)時(shí)間是1秒,b系統(tǒng)60% 的平均響應(yīng)時(shí)間是1秒,那么a系統(tǒng)會(huì)有10% 的用戶不滿意而b系統(tǒng)有40% 用戶不滿意嗎?如果 a 系統(tǒng)中,90% 的響應(yīng)時(shí)間是0.91秒; 在 b系統(tǒng) 中,90%的響應(yīng)時(shí)間是1.07秒,那么, 這樣的描述比僅僅說(shuō)1.00秒的平均響應(yīng)時(shí)間更有信息量。
我們嘗試用可能的兩個(gè)數(shù)來(lái)描述世界,一個(gè)是均值,一個(gè)是方差??蛻舾惺艿降目赡苁欠讲?#xff0c;而不是均值。將響應(yīng)時(shí)間表示為百分?jǐn)?shù),可以產(chǎn)生與最終用戶期望相符的性能描述,而且令人信服, 例如,”動(dòng)態(tài)庫(kù)加載”的任務(wù)必須在至少99.99% 的執(zhí)行中在小于0.5秒的時(shí)間內(nèi)完成。
我們同樣用概率來(lái)描述性能,或許,一切的抽象,可能都?xì)w于數(shù)學(xué),一切的結(jié)果,可能都?xì)w于概率。
四、問(wèn)題診斷——以終為始
在曾經(jīng)遇到的性能問(wèn)題中,大多數(shù)是關(guān)于響應(yīng)時(shí)間的: “過(guò)去做某事只需要不到一秒的時(shí)間,現(xiàn)在有時(shí)候需要10多秒?!?當(dāng)然,一個(gè)更樸實(shí)的說(shuō)法是,“整個(gè)系統(tǒng)太慢了,簡(jiǎn)直不能使用?!?/p>
關(guān)于性能問(wèn)題的診斷,最重要的事情是清楚地陳述問(wèn)題,明確了問(wèn)題的描述,才能清楚地思考問(wèn)題。
以終為始,系統(tǒng)想要達(dá)到的目標(biāo)狀態(tài)是什么呢?找出一些可以用來(lái)表達(dá)目標(biāo)狀態(tài)的細(xì)節(jié)數(shù)據(jù): 例如,“在許多情況下,系統(tǒng)的響應(yīng)時(shí)間不超過(guò)2秒。如果至少有95% 的關(guān)鍵任務(wù)響應(yīng)應(yīng)時(shí)間在一秒以內(nèi),這才是我們所要的?!?/p>
這樣的描述看起來(lái)不錯(cuò),但是:
-
如果用戶沒(méi)有這樣一個(gè)定量目標(biāo)呢?
-
這個(gè)特定的目標(biāo)有兩個(gè)量(1s和95%) , 如果不知道其中的某一個(gè)該怎么辦呢?
-
更糟糕的是,如果用戶確實(shí)有特定的想法,但是這些期望是不可能實(shí)現(xiàn)的,又該怎么辦呢?
-
如何怎么知道什么是“可能的”或“不可能的” ?......
性能的問(wèn)題診斷從問(wèn)題的描述, 以終為始,循序逆推,接下來(lái)才是使用工具來(lái)應(yīng)對(duì)這些問(wèn)題。
時(shí)間利器——時(shí)序圖
時(shí)序圖是 UML中指定的一種圖形,用于按照交互發(fā)生的順序顯示對(duì)象之間的交互。在可視化響應(yīng)時(shí)間方面,時(shí)序圖是一個(gè)非常有用的工具。
考慮一下繪制時(shí)序圖的比例,每個(gè)進(jìn)入的“請(qǐng)求”箭頭和相應(yīng)的“響應(yīng)”箭頭之間的距離與服務(wù)請(qǐng)求所花費(fèi)的時(shí)間成正比,可以說(shuō)明圖中表示的組件是如何花費(fèi)時(shí)間的,可以“感覺(jué)”到響應(yīng)時(shí)間的相對(duì)貢獻(xiàn)。
時(shí)序圖可以幫助人們概念化響應(yīng)時(shí)間在給定的系統(tǒng)中是如何被消耗的,還可以很好地顯示同步處理線程是如何并行工作的,除了分析業(yè)務(wù),也是性能分析的好工具。但要系統(tǒng)性思考性能,還需要一些其他的東西。假設(shè),要修復(fù)任務(wù)的響應(yīng)時(shí)間為2048秒,在這段時(shí)間內(nèi),運(yùn)行該任務(wù)將導(dǎo)致應(yīng)用程序服務(wù)器執(zhí)行了320,000個(gè)數(shù)據(jù)庫(kù)調(diào)用。圖3顯示了這個(gè)任務(wù)的時(shí)序圖。
在應(yīng)用程序和數(shù)據(jù)庫(kù)層之間有太多的請(qǐng)求和響應(yīng)箭頭,以至于看不到任何細(xì)節(jié)。也就是說(shuō), 在一個(gè)很長(zhǎng)的滾動(dòng)條上打印時(shí)序圖并不是一個(gè)有用的解決方案。
時(shí)序圖是一個(gè)很好的工具來(lái)概念化控制流和相應(yīng)的時(shí)間流,可以作為時(shí)間上的利刃,那么有空間利刃么?
空間分析——組件描述直方圖
為了處理那些需要大量調(diào)用的任務(wù),需要一個(gè)方便的時(shí)序集合,這樣就能理解時(shí)間如何花費(fèi)的重要模式。概要描述是響應(yīng)時(shí)間的表格分解,通常按組件響應(yīng)時(shí)間貢獻(xiàn)降序列出。
直方圖一般可以確切地顯示慢速任務(wù)在哪里消耗了時(shí)間。例如,可以推導(dǎo)出概要描述中標(biāo)識(shí)的每個(gè)函數(shù),以及函數(shù)調(diào)用響應(yīng)時(shí)間所占的百分比,還可以推導(dǎo)出任務(wù)期間每種類型的函數(shù)調(diào)用的平均響應(yīng)時(shí)間。
如果可以深入到聚合為單個(gè)調(diào)用中持續(xù)時(shí)間,就可以知道有多少這些調(diào)用對(duì)應(yīng)于某個(gè)函數(shù)的其他調(diào)用,并且可以知道每個(gè)調(diào)用消耗了多少響應(yīng)時(shí)間。“這個(gè)任務(wù)應(yīng)該運(yùn)行多長(zhǎng)時(shí)間? ” 使用組件描述直方圖,可以構(gòu)造問(wèn)題的答案。
老碼農(nóng)認(rèn)為,這是問(wèn)題診斷的第一個(gè)重要問(wèn)題,這是解決性能問(wèn)題的開端。
五、優(yōu)化原則——要事優(yōu)先?
性能改進(jìn)與程序使用所改進(jìn)東西的程度成正比。如果正在嘗試改進(jìn)的事情只占任務(wù)總響應(yīng)時(shí)間的5% ,那么能夠產(chǎn)生的最大影響也緊緊是總響應(yīng)時(shí)間的5% 。這意味著,我們?cè)綄⒔裹c(diǎn)集中在直方圖的頂部(假設(shè)組件直方圖按響應(yīng)時(shí)間降序排列) ,整體響應(yīng)時(shí)間的潛在好處就越大。
但是,這并不意味著總是按照自上而下的順序處理組件的響應(yīng),還需要考慮執(zhí)行補(bǔ)救措施的成本。考慮組件的響應(yīng)時(shí)間直方圖,添加最佳補(bǔ)救方法可以節(jié)省多少時(shí)間,可以看到每個(gè)補(bǔ)救方法的實(shí)現(xiàn)成本。
確立優(yōu)化起點(diǎn)
那么,先采取什么補(bǔ)救措施?成本核算,尋找更好的凈收益,這才是真正需要的優(yōu)化點(diǎn)。
帶有改進(jìn)成本的組件響應(yīng)時(shí)間直方圖打開了一扇大門,讓我們可以就首先實(shí)施哪些補(bǔ)救措施做出更好的決定,為預(yù)測(cè)改進(jìn)后的性能指標(biāo)提供了一個(gè)尺度。進(jìn)一步,可以找到比預(yù)期更有效的方法,以低于預(yù)期的成本縮短響應(yīng)時(shí)間。
首先采取什么補(bǔ)救措施取決于對(duì)成本估算的信任程度?!胺浅1阋恕笔欠裾娴目紤]到了所提議的改進(jìn)可能對(duì)系統(tǒng)造成的風(fēng)險(xiǎn)呢?例如,改變這個(gè)參數(shù)或者刪除那個(gè)索引看起來(lái)非常經(jīng)濟(jì),但是這個(gè)改變是否有潛在的破壞性?改變了一些現(xiàn)在甚至沒(méi)有想到的組件的良好性能呢?可靠的成本估算是技術(shù)能力得到體現(xiàn)的另一個(gè)領(lǐng)域。
循序漸進(jìn)中的信譽(yù)
另一個(gè)值得考慮的因素是可以通過(guò)創(chuàng)造小的勝利來(lái)獲得的信譽(yù)。也許低成本、低風(fēng)險(xiǎn)的改進(jìn)不會(huì)帶來(lái)總體響應(yīng)時(shí)間的改進(jìn),但是它建立一個(gè)小改進(jìn)的跟蹤記錄,完全符合對(duì)于為緩慢的任務(wù)節(jié)省多少響應(yīng)時(shí)間的預(yù)測(cè),也是有價(jià)值的。在軟件性能領(lǐng)域,預(yù)測(cè)和最終實(shí)現(xiàn)的跟蹤記錄能夠帶來(lái)必要的可信度,以影響我們的同事甚至經(jīng)理、客戶等等,他們會(huì)支持你采取越來(lái)越昂貴的補(bǔ)救措施,為企業(yè)帶來(lái)更大的回報(bào)。
需要注意的是,當(dāng)提出更大而昂貴、高風(fēng)險(xiǎn)的補(bǔ)救方案且獲得支持時(shí),要小心謹(jǐn)慎。信譽(yù)是脆弱的,建立很難,但推倒只需要一瞬。
減少相干風(fēng)險(xiǎn)
在實(shí)踐中,常常會(huì)出現(xiàn)修復(fù)一個(gè)任務(wù)的性能后,結(jié)果損害了另一個(gè)任務(wù)的性能。那么,在性能優(yōu)化的時(shí)候,應(yīng)該注意些什么呢?
這里,可以類比一個(gè)這樣的問(wèn)題:“為了感覺(jué)涼快,是該打開窗子還是脫掉厚衣服呢?”
這就是性能優(yōu)化的最小化風(fēng)險(xiǎn)原則,確保自己本地的東西是有秩序的,盡量縮小故障域的范圍。如果除了使用一兩個(gè)程序之外,所有程序都處理得很好,那么最安全的解決方案就是將范圍本地化在這一兩個(gè)程序的修改上。
六、性能中的時(shí)空因素
在具體的性能優(yōu)化過(guò)程中,會(huì)遇到各種各樣的情況,常見要素包括數(shù)據(jù)傾斜、執(zhí)行效率、負(fù)載和延遲。
數(shù)據(jù)傾斜
當(dāng)處理處理組件響應(yīng)時(shí)間直方圖的時(shí)候,可能反復(fù)遇到這樣的問(wèn)題: x個(gè)數(shù)據(jù)庫(kù)調(diào)用占用了y秒的響應(yīng)時(shí)間。如果能消除一半的調(diào)用,能消除多少不必要的響應(yīng)時(shí)間呢?答案往往出人意料,幾乎從來(lái)不是“一半的響應(yīng)時(shí)間”, 取決于我們可以消除的單個(gè)調(diào)用的響應(yīng)時(shí)間。不能假設(shè)每個(gè)調(diào)用的持續(xù)時(shí)間是平均y/x秒,語(yǔ)句沒(méi)有告訴我們調(diào)用持續(xù)時(shí)間是一致的。
數(shù)據(jù)傾斜是具體調(diào)用中的不一致性,出現(xiàn)傾斜的可能性使得無(wú)法對(duì)組件響應(yīng)時(shí)間提供準(zhǔn)確的答案。在不了解任何有關(guān)數(shù)據(jù)傾斜信息的條件下,可以提供的答案是,“在0到y(tǒng)秒之間的某個(gè)位置。但是,假設(shè)有具體的附加信息。就可以制定出更精確的最佳情況和最差情況估計(jì)。在數(shù)據(jù)庫(kù)應(yīng)用中,讀寫分離也只是大粒度分隔數(shù)據(jù)傾斜的一種方式。
運(yùn)行效率
即使整個(gè)系統(tǒng)中的每個(gè)人都很痛苦,仍然應(yīng)該首先關(guān)注業(yè)務(wù)需要修復(fù)的程序。起點(diǎn)是確保程序盡可能高效地工作。在不增加容量和不犧牲業(yè)務(wù)功能的情況下, 效率與可消除多少任務(wù)執(zhí)行的總服務(wù)時(shí)間成反比。換句話說(shuō),效率與浪費(fèi)成反比。
以下是數(shù)據(jù)庫(kù)應(yīng)用程序中經(jīng)常出現(xiàn)的2個(gè)有關(guān)浪費(fèi)例子:
-
中間層程序?yàn)槊恳恍袛?shù)據(jù)庫(kù)插入創(chuàng)建了一個(gè)獨(dú)立的 SQL 語(yǔ)句。它執(zhí)行了1000個(gè)數(shù)據(jù)庫(kù)prepare調(diào)用也就是1000個(gè)網(wǎng)絡(luò)IO調(diào)用 ,而本可以通過(guò)一個(gè)調(diào)用從而減少999個(gè)網(wǎng)絡(luò)IO調(diào)用來(lái)完成這項(xiàng)工作。
-
一條 SQL 語(yǔ)句涉及了數(shù)據(jù)庫(kù)緩沖上萬(wàn)次,以返回一個(gè)幾百行的結(jié)果集。而一個(gè)額外的過(guò)濾語(yǔ)句可以返回終端用戶真正想要看到的6行,只對(duì)數(shù)據(jù)庫(kù)緩沖區(qū)訪問(wèn)進(jìn)行幾十次次觸摸。
當(dāng)然,如果一個(gè)系統(tǒng)存在某些全局性問(wèn)題,例如,考慮不周的索引、設(shè)置糟糕的參數(shù)、配置糟糕的硬件等等,會(huì)導(dǎo)致整個(gè)系統(tǒng)的大量任務(wù)效率低下,那么應(yīng)該修復(fù)它。但是,不要為了適應(yīng)效率低下的程序而調(diào)整系統(tǒng),不要用權(quán)宜之計(jì)作為永久的解決方案。
解決效率低下的問(wèn)題往往在解決程序本身效率低下的問(wèn)題上。即使某些程序是商業(yè)化的現(xiàn)成應(yīng)用程序,從長(zhǎng)遠(yuǎn)來(lái)看,要與軟件供應(yīng)商合作使程序更有效,而不是試圖優(yōu)化系統(tǒng),使其盡可能高效地處理固有的低效率程序。
使程序更高效可以為系統(tǒng)中的每個(gè)人帶來(lái)巨大的好處,很容易看出減少浪費(fèi)是如何幫助修復(fù)任務(wù)的響應(yīng)時(shí)間的。
工作負(fù)載
許多人也不明白的是,讓一個(gè)程序變得更有效率,會(huì)給系統(tǒng)中其他程序帶來(lái)性能改進(jìn),而這些程序與正在修復(fù)的程序沒(méi)有明顯的關(guān)系。這是由于負(fù)載對(duì)系統(tǒng)的影響。
負(fù)載是由并發(fā)任務(wù)執(zhí)行引起的資源競(jìng)爭(zhēng)。這就是為什么我們的性能測(cè)試不能捕捉到生產(chǎn)后期出現(xiàn)的所有性能問(wèn)題的原因。
負(fù)載的一個(gè)度量是利用率,即資源使用除以指定時(shí)間間隔內(nèi)的資源容量。隨著資源利用率的提高,用戶從該資源請(qǐng)求服務(wù)時(shí)的響應(yīng)時(shí)間也會(huì)增加。任何一個(gè)在高峰時(shí)間在北京開過(guò)車的人都經(jīng)歷過(guò)這種現(xiàn)象,當(dāng)交通非常擁擠時(shí),必須在紅綠燈等候更長(zhǎng)的時(shí)間。
軟件慢下來(lái)和汽車是不一樣的,汽車在繁忙的交通中時(shí)速30英里而在開闊的道路上時(shí)速60英里。由于CPU的每個(gè)時(shí)鐘周期有固定的指令數(shù)量,計(jì)算機(jī)軟件總是以同樣的速度運(yùn)行,但是響應(yīng)時(shí)間肯定會(huì)隨著系統(tǒng)資源的使用增加而減少。
還是時(shí)空的糾纏,隨著負(fù)載的增加,系統(tǒng)變慢的原因有兩個(gè): 排隊(duì)延遲和一致性延遲。
排隊(duì)延遲
負(fù)載和響應(yīng)時(shí)間之間的數(shù)學(xué)關(guān)系是眾所周知的。一個(gè)稱為 M/M/m 的排隊(duì)模型將響應(yīng)時(shí)間與滿足一組特定需求的系統(tǒng)負(fù)載聯(lián)系了起來(lái)。M/M/m 有一個(gè)假設(shè),即系統(tǒng)具有“理論上完美的可伸縮性” ,盡管有一些過(guò)分,但 M/M/m 模型在性能方面還是有很多值得我們學(xué)習(xí)的地方。下圖顯示了m=8時(shí)該模型的響應(yīng)時(shí)間和負(fù)載之間的關(guān)系。
在上圖中,可以從數(shù)學(xué)上看出在不同負(fù)載條件下使用系統(tǒng)時(shí)的感受。在低負(fù)載時(shí),響應(yīng)時(shí)間基本上與空負(fù)載時(shí)的響應(yīng)時(shí)間相同。隨著負(fù)載的增加,可以感覺(jué)到響應(yīng)時(shí)間出現(xiàn)了輕微的、逐漸的降低。這種逐漸的退化并沒(méi)有造成太大的危害,但是隨著負(fù)載持續(xù)上升,響應(yīng)時(shí)間開始以一種既不輕微也漸變的方式退化。相反,這種退化令人不爽,而且實(shí)際上是雙曲線的。
在完美的可伸縮性M/M/m 模型中,響應(yīng)時(shí)間(r)由兩個(gè)部分組成: 服務(wù)時(shí)間(s)和排隊(duì)延遲(q)。服務(wù)時(shí)間是任務(wù)消耗給定資源的時(shí)間,以每個(gè)任務(wù)執(zhí)行的時(shí)間為單位。排隊(duì)延遲是指任務(wù)在排隊(duì)等待使用給定資源的時(shí)間。排隊(duì)延遲也以每個(gè)任務(wù)執(zhí)行的時(shí)間來(lái)度量,是指給定任務(wù)的響應(yīng)時(shí)間與否則就會(huì)卸載系統(tǒng)上同一任務(wù)的響應(yīng)時(shí)間之間的差異(不要忘記我們完美的可伸縮性假設(shè))。
一致性延遲
相干延遲是由于任務(wù)的有序性執(zhí)行造成的時(shí)間延遲,不能使用M/M/m那樣的排隊(duì)模型。這是因?yàn)?M/M/m 假定所有 m 的服務(wù)通道都是并行、同構(gòu)而且獨(dú)立的,意味著模型中假設(shè)了當(dāng)你在先進(jìn)先出隊(duì)列中等待足夠長(zhǎng)的時(shí)間,并且前面排隊(duì)的所有請(qǐng)求都已經(jīng)退出服務(wù)隊(duì)列后,會(huì)輪到你接受服務(wù)。然而,相干性延遲并不是這樣工作的。
假設(shè)有一個(gè) HTML 數(shù)據(jù)輸入表單,其中一個(gè)“ Update”的按鈕執(zhí)行 SQL Update 語(yǔ)句,另一個(gè)“ Save”的按鈕執(zhí)行 SQL commit 語(yǔ)句,這樣構(gòu)建的應(yīng)用程序幾乎可以確認(rèn)性能的糟糕程度。這對(duì)于希望更新同一行數(shù)據(jù)的其他任務(wù)來(lái)說(shuō),影響可能是毀滅性的。每個(gè)任務(wù)都必須等待該行上的鎖定(或者,在某些系統(tǒng)上,是更糟糕的頁(yè)鎖定) ,直到鎖定用戶決定繼續(xù)并單擊 “save”,或者直到數(shù)據(jù)庫(kù)管理員終止用戶的會(huì)話。
在這種情況下,任務(wù)等待釋放鎖的時(shí)間長(zhǎng)短與系統(tǒng)有多忙無(wú)關(guān),取決于系統(tǒng)各種資源利用之外的隨機(jī)因素。這就是為什么永遠(yuǎn)不能假設(shè)在單元測(cè)試環(huán)境中執(zhí)行的性能測(cè)試足以決定是否將新代碼插入生產(chǎn)系統(tǒng)。
七、理解性能拐點(diǎn)
回歸到有關(guān)性能的兩個(gè)最重要的指標(biāo):
-
最佳響應(yīng)時(shí)間:用戶不想為了完成任務(wù)而等待太長(zhǎng)時(shí)間。
-
最佳吞吐量:希望盡可能多的人能夠同時(shí)運(yùn)行他們的任務(wù)。
如前所述,這兩個(gè)目標(biāo)是矛盾的。優(yōu)化第一個(gè)目標(biāo)需要最小化系統(tǒng)的負(fù)載; 優(yōu)化第二個(gè)目標(biāo)需要最大化負(fù)載。介于兩者之間的某個(gè)負(fù)載級(jí)別可能是系統(tǒng)的最佳負(fù)載。
發(fā)生這種最佳平衡的資源利用值稱為性能拐點(diǎn)。此時(shí),吞吐量最大化,對(duì)響應(yīng)時(shí)間的負(fù)面影響最小。在數(shù)學(xué)上,拐點(diǎn)是響應(yīng)時(shí)間除以利用率達(dá)到最小值。拐點(diǎn)的一個(gè)很好的屬性是,它發(fā)生在通過(guò)原點(diǎn)的一條直線與響應(yīng)時(shí)間曲線相切的某一點(diǎn)上。
為什么拐點(diǎn)如此重要?對(duì)于具有隨機(jī)服務(wù)請(qǐng)求的系統(tǒng),允許持續(xù)的資源負(fù)載超過(guò)拐點(diǎn)會(huì)導(dǎo)致響應(yīng)時(shí)間和吞吐量隨著負(fù)載的微小變化而劇烈波動(dòng)。因此,對(duì)于具有隨機(jī)請(qǐng)求到達(dá)的系統(tǒng),管理負(fù)載以使其不超過(guò)拐點(diǎn)是至關(guān)重要的。
即使系統(tǒng)可以完美地伸縮,一旦平均負(fù)載超過(guò)拐點(diǎn),仍然會(huì)遇到大量的性能問(wèn)題。更何況,實(shí)踐的系統(tǒng)遠(yuǎn)不如模型中的假設(shè)。因此,性能拐點(diǎn)的利用率值更具約束性。
總結(jié)一下:
-
系統(tǒng)中的每一個(gè)資源都有一個(gè)拐點(diǎn)。
-
在一個(gè)隨機(jī)請(qǐng)求的系統(tǒng)中,如果允許系統(tǒng)中任何資源的持續(xù)利用率超過(guò)拐點(diǎn)值,就會(huì)遇到性能問(wèn)題。
因此,負(fù)載管理是至關(guān)重要的,這樣的資源利用率就不會(huì)超過(guò)系統(tǒng)的拐點(diǎn)。
八、容量規(guī)劃保性能
容量規(guī)劃是一個(gè)復(fù)雜一些的技術(shù),有如下的目標(biāo)約束:
-
對(duì)給定資源的目標(biāo)容量是在高峰時(shí)間可以流暢地完成任務(wù),而不需超過(guò)拐點(diǎn)。
-
如果利用率低于拐點(diǎn),系統(tǒng)性能大致呈線性。
-
如果系統(tǒng)運(yùn)行的任何資源超出了它們的拐點(diǎn)范圍,無(wú)論是否意識(shí)到這些問(wèn)題,都會(huì)存在性能問(wèn)題。
-
如果存在性能問(wèn)題,不需要花時(shí)間在數(shù)學(xué)模型上,而是通過(guò)重新安排負(fù)載、減少負(fù)載或增加容量來(lái)盡快修復(fù)這些問(wèn)題。
你可能已經(jīng)注意到,我多次使用隨機(jī)到達(dá)這個(gè)術(shù)語(yǔ)。為什么這很重要?
有些系統(tǒng)可能沒(méi)有完全確定的作業(yè)計(jì)劃。如果訪問(wèn)者可以完全確定地進(jìn)入系統(tǒng),這意味著可以準(zhǔn)確地知道下一個(gè)服務(wù)請(qǐng)求什么時(shí)候到達(dá),那么就可以使資源利用率臨時(shí)超過(guò)閾值,而不一定會(huì)造成性能問(wèn)題。在一個(gè)具有確定性到達(dá)的系統(tǒng)上,目標(biāo)是100% 的資源利用率,而不是將如此多的工作負(fù)載進(jìn)行排隊(duì)。
拐點(diǎn)之所以在隨機(jī)訪問(wèn)的系統(tǒng)中如此重要,是因?yàn)樗鼈儍A向于聚集并導(dǎo)致短暫的利用率峰值。這些峰值需要足夠的空閑容量來(lái)消耗,這樣才能使用戶不必忍受每次峰值發(fā)生時(shí)明顯的隊(duì)列延遲(這會(huì)導(dǎo)致響應(yīng)時(shí)間的明顯波動(dòng))。
對(duì)于給定的資源,只要持續(xù)時(shí)間不超過(guò)幾秒鐘,利用率的暫時(shí)上升超過(guò)拐點(diǎn)是可以的。那么,多少秒是多呢?如果無(wú)法滿足基于百分比的響應(yīng)時(shí)間承諾或者吞吐量承諾,那么峰值持續(xù)時(shí)間就太長(zhǎng)了。根據(jù)經(jīng)驗(yàn),應(yīng)該至少確保峰值持續(xù)時(shí)間不超過(guò)8秒。
九、性能測(cè)試
關(guān)于排隊(duì)延遲和一致性延遲的討論導(dǎo)致了一個(gè)非常困難的問(wèn)題: 如何才能對(duì)一個(gè)新應(yīng)用程序進(jìn)行足夠的測(cè)試,以確保不會(huì)因?yàn)樾阅軉?wèn)題而破壞生產(chǎn)環(huán)境呢?
一切模型都不會(huì)是完美的, 性能測(cè)試可能是困難的,在這些模型和性能測(cè)試中,很可能在實(shí)際生產(chǎn)中遇到這些問(wèn)題之前預(yù)見問(wèn)題。
有些人認(rèn)為這種性能測(cè)試是徒勞的,因此完全有理由不進(jìn)行測(cè)試。千萬(wàn)不要陷入這種心態(tài), 因?yàn)?
-
如果試圖在生產(chǎn)環(huán)境上線前發(fā)現(xiàn)問(wèn)題,會(huì)發(fā)現(xiàn)更多的問(wèn)題,而不是不去嘗試。
-
在性能測(cè)試中,盡管永遠(yuǎn)不會(huì)發(fā)現(xiàn)所有的問(wèn)題, 但這才是為什您需要一個(gè)可靠而高效的方法來(lái)解決上線前測(cè)試過(guò)程中的泄漏問(wèn)題。
不要跳過(guò)性能測(cè)試。至少,當(dāng)解決上線操作過(guò)程中不可避免會(huì)出現(xiàn)的性能問(wèn)題時(shí),性能測(cè)試計(jì)劃將使我們成為更有能力的診斷專家和更清晰的思考者。
再次回歸到吞吐量和響應(yīng)時(shí)間,吞吐量通常容易測(cè)量,響應(yīng)時(shí)間則要困難得多。用秒表計(jì)算終端用戶操作的時(shí)間可能并不困難,但是要得到真正需要的東西可能非常困難,這就是為什么要深入研究響應(yīng)時(shí)間的細(xì)節(jié)。
不幸的是,人們傾向于測(cè)量容易測(cè)量的東西,而不一定是他們應(yīng)該測(cè)量的東西。在這里,糟糕并不意味著永遠(yuǎn)不能工作。實(shí)際上,如果替代措施從來(lái)都不起作用,情況會(huì)更好。這樣就沒(méi)人會(huì)用了。問(wèn)題在于,代理測(cè)量有時(shí)會(huì)起作用。這激發(fā)了人們的信心,他們使用的措施應(yīng)該一直工作,然后他們沒(méi)有。代理測(cè)量有兩大問(wèn)題。
當(dāng)需要評(píng)估一個(gè)真實(shí)系統(tǒng)的細(xì)節(jié)時(shí),取決于系統(tǒng)允許獲得的測(cè)量數(shù)據(jù)有多好。
十、性能是一個(gè)功能
最后,希望性能被看作是一個(gè)軟件應(yīng)用的功能,就像在 bug 跟蹤系統(tǒng)中所展示的那樣。然而,像許多其他特性一樣,在編寫、學(xué)習(xí)、設(shè)計(jì)和創(chuàng)建應(yīng)用程序的時(shí)候,無(wú)法確切地知道系統(tǒng)性能是怎樣的。對(duì)于許多應(yīng)用程序 ,直到軟件進(jìn)入生產(chǎn)階段,性能仍然是完全未知的。
既然不知道應(yīng)用程序在生產(chǎn)環(huán)境中的性能如何,那么在編寫程序的時(shí)候,要考慮如果在生產(chǎn)環(huán)境中輕松地修復(fù)性能問(wèn)題。編寫一個(gè)在生產(chǎn)環(huán)境中容易修復(fù)的應(yīng)用程序,一般是從一個(gè)在生產(chǎn)環(huán)境中容易測(cè)量的應(yīng)用程序開始的。
通常,當(dāng)提到生產(chǎn)環(huán)境性能度量的時(shí)候,人們會(huì)對(duì)性能度量的侵入效應(yīng)感到擔(dān)憂。有額外代碼路徑來(lái)度量計(jì)時(shí)的軟件不會(huì)比沒(méi)有額外代碼路徑的軟件慢嗎?過(guò)早的優(yōu)化是一切罪惡的根源么?!將性能度量整合到產(chǎn)品中更有可能創(chuàng)建一個(gè)快速的應(yīng)用程序,更重要的是,一個(gè)隨著時(shí)間推移會(huì)變得更快的應(yīng)用程序。
性能,就像任何其他特性一樣,不會(huì)自然而然地發(fā)生,必須經(jīng)過(guò)設(shè)計(jì)和構(gòu)建。要做好性能,必須考慮它,研究它,為它編寫額外的代碼,測(cè)試它,并最終得到它。
?
作者丨半吊子全棧工匠
來(lái)源丨喔家ArchiSelf(ID:wireless_com)
總結(jié)
以上是生活随笔為你收集整理的也许90%的人都没有真正搞懂性能优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 说了这么多次 I/O,可你知道其中的原理
- 下一篇: 看完这篇,你也是字符编码大神!