本文翻譯自 Thinking Clearly About Performance 這是我三年前讀到的一篇關(guān)于性能問題的好文抑党,讀完后還覺不過癮,怕理解的不夠遂又翻譯了一遍底靠,這也是當(dāng)年我的第一次翻譯。
這幾年來每次碰到性能問題暑中,我都會想起這篇文章,它并不像很多其他關(guān)于性能問題的文章稻轨,告訴你利用什么工具怎么去解決性能問題,這類文章更多屬于「術(shù)」的層面殴俱,而術(shù)的層面在不同的技術(shù)棧會有很不同的選擇枚抵。而本文則高屋建瓴的幫助讀者建立起對性能的正確認(rèn)識线欲,從而能夠獲得更全面的視角去看待和思考性能問題汽摹。這是「道」的層面,正所謂道法自然趴泌,術(shù)變?nèi)f千舟舒,深刻理解了「道」踱讨,那么面對性能問題的萬千之「術(shù)」才不會那么茫然。
文章略長痹筛,建議先收藏,稍后合適時抽出一塊時間來細(xì)細(xì)讀之谣旁,當(dāng)有所獲滋早。
摘要
對于開發(fā)者榄审、技術(shù)管理者杆麸、架構(gòu)師、系統(tǒng)分析師和項目經(jīng)理來說饼问,創(chuàng)建具備高性能特征的復(fù)雜軟件都是一件極其困難的事。然而莱革,通過了解一些基本原理讹开,性能問題的解決和預(yù)防可以更簡單可靠。本文講述了這些基本原理旦万,涵蓋了一系列的目標(biāo)、術(shù)語成艘、工具和決策,綜合利用好它們來最大可能的創(chuàng)建一個長期有效的高性能應(yīng)用狰腌。本文的一些例子來自于 Oracle 的經(jīng)驗,但本文的范圍并不局限于 Oracle 的產(chǎn)品瑰枫。
目錄
- 公理化方法
- 什么是性能?
- 響應(yīng)時間 VS. 吞吐量
- 百分比指標(biāo)
- 問題診斷
- 序列圖
- 性能剖析
- 阿姆達(dá)爾定律
- 偏斜度
- 最小化風(fēng)險
- 效率
- 負(fù)載
- 隊列延遲
- 拐點
- 拐點的相關(guān)性
- 容量規(guī)劃
- 隨機到達(dá)
- 相關(guān)性延遲
- 性能測試
- 測量
- 性能是一項功能特性
- 尾聲:關(guān)于「拐點」的公開辯論
- 關(guān)于作者
- 參考
1. 公理化方法
當(dāng)我在 1989 年加入 Oracle 公司時光坝,解決性能問題(人們通常說的是 Oracle 調(diào)優(yōu))是很困難的。 只有少部分人聲稱他們很擅長這個盯另,很多人都去咨詢他們。 當(dāng)時商蕴,我進(jìn)到 Oracle 調(diào)優(yōu)這個領(lǐng)域時芝发,我完全沒準(zhǔn)備好绪商。 最近我又開始對 MySQL 進(jìn)行調(diào)優(yōu)辅鲸,這看起來和我 20 年前在 Oracle 公司做的差不多。
它讓我想起了當(dāng)我 13 歲剛接觸代數(shù)學(xué)時是多么的困難独悴。 在那個年齡我只能依靠「數(shù)學(xué)直覺」來解決類似 3x + 4 = 13 這樣的方程。 問題是我們之中大部分人都沒有所謂的「數(shù)學(xué)直覺」决采。 我記得當(dāng)看到這樣的問題: 3x + 4 = 13 求解 x落蝙,只能采用試錯法偶然發(fā)現(xiàn) x 應(yīng)該是3暂幼。
試錯法給我的感覺雖然能解決一些簡單的方程式筏勒,但很慢而且不爽旺嬉。 一旦等式稍有變化如 3x + 4 = 14,試錯法就不能適應(yīng)邪媳。 那么該怎么辦呢?當(dāng)時我沒有好好思考過迅涮,直到 15 歲時 James R. Harkey 指引我走上正確的道路徽龟。
Harkey 先生教會我使用公理方法來解決代數(shù)方程問題。他給我們展示了一系列的步驟(還給了我很多家庭作業(yè)進(jìn)行練習(xí))。做作業(yè)時除了記錄下這些步驟传透,還要寫下我們是如何思考的。這樣我們不僅自己想得很清楚群嗤,而且通過一系列可靠的、可重復(fù)的步驟來向閱讀我們作業(yè)的人證明了我們確實搞明白了狂秘。 Harkey 先生看到的我的作業(yè)像下面這樣:
3.1x + 4 = 13
待求解方程
3.1x + 4 - 4 = 13 - 4
減去相等的值
3.1x = 9
加法逆運算闰围,化簡
3.1x ∕ 3.1 = 9 ∕ 3.1
除以相等的值
x ≈ 2.903
乘法逆運算,化簡求解
這就是 Harkey 先生教導(dǎo)的適用于代數(shù)學(xué)羡榴、幾何學(xué)、三角學(xué)和微積分的公理化方法校仑。 由一系列符合邏輯的、可證明的和可審計的小步驟組成稻扬。這是我第一次真正從數(shù)學(xué)中學(xué)到的東西扁位。
自然,當(dāng)時我沒能認(rèn)識到其中的價值闻坚,但證明作為一種技能對我后來的成功至關(guān)重要逝她。我發(fā)現(xiàn)在生活中睬捶,知道一件事很重要,但能向別人講清楚(證明)更重要擒贸。沒有好的證明技能,就很難成為一名好的顧問介劫、好的領(lǐng)導(dǎo)甚至好的員工。
我在上世紀(jì) 90 年代中期的目標(biāo)是為 Oracle 性能優(yōu)化創(chuàng)建一套類似的险绘、嚴(yán)格的公理化方法。后來我將其擴展到了 Oracle 之外隆圆,建立了一套適用于所有計算機軟件性能優(yōu)化的公理化方法。好吧渺氧,我發(fā)現(xiàn)并非所有人都喜歡這種說法,那我們換一種說法:
我們的目標(biāo)就是幫助你想清楚如何優(yōu)化你的軟件系統(tǒng)性能白华。
2. 什么是性能?
假如你去 Google 下 Performance 這個關(guān)鍵字弧腥,可能會得到 5 億個鏈接潮太。 其中涉及的內(nèi)容范圍可能從自行車比賽到可怕的員工審查流程(如今很多公司已經(jīng)學(xué)會了避免這個流程)。但假如我去 Google 下 Performance 這個關(guān)鍵字铡买,大部分的首頁鏈接都會與這篇文章的主題有關(guān):計算機軟件執(zhí)行無論何種任務(wù)所花費的時間。
任務(wù)這個詞是一個很適合的開始奇钞。任務(wù)是一個面向業(yè)務(wù)的工作單元。任務(wù)能夠嵌套:打印發(fā)貨單是一個任務(wù)媒至,打印一張發(fā)貨單(一個子任務(wù))也是一個任務(wù)。當(dāng)一個用戶說起性能時拒啰,他通常指的是系統(tǒng)執(zhí)行一系列任務(wù)所花費的時間蒂胞。響應(yīng)時間 是任務(wù)的執(zhí)行時長条篷,用每個任務(wù)的時間來度量骗随,像每點擊秒數(shù)赴叹。例如我用 Google 搜索關(guān)鍵字 Performance 的響應(yīng)時間是 0.24 秒。 這個數(shù)據(jù)來自我的瀏覽器它渲染完 Google 網(wǎng)頁花費的時間涨椒,那么很明顯,這量化了我對 Google 性能的直覺感知蚕冬。
一些人對另外一個性能指標(biāo)很感興趣:吞吐量。 吞吐量 是在一個特定時間段內(nèi)完成的任務(wù)的計數(shù)囤热,例如:每秒點擊數(shù)。通常為一群人提供服務(wù)比為個別人提供服務(wù)的人更關(guān)心吞吐量锨苏。例如,一個獨立會計會更關(guān)心日報的響應(yīng)時間是否會導(dǎo)致今晚需要加班伞租,而會計部的經(jīng)理更關(guān)心系統(tǒng)的是否能支撐所有的會計處理完今天的數(shù)據(jù)限佩。
3. 響應(yīng)時間 VS. 吞吐量
通常來講,響應(yīng)時間和吞吐量是一個倒數(shù)關(guān)系(響應(yīng)時間越長吞吐量越低)祟同,但這并不確切。 實際情況更微妙耐亏、復(fù)雜一些。
例 1
假如暇矫,在一些性能基準(zhǔn)測試中,你的系統(tǒng)的測量結(jié)果是每秒能處理 1000 個任務(wù)李根,那么用戶的平均響應(yīng)時間是多少几睛? 你可能會說平均響應(yīng)時間等于 1 / 1000 = 0.001 秒/每任務(wù),但它真不是這樣的所森。
假如在你的系統(tǒng)內(nèi)部擁有 1000 個相同的、獨立的焕济、并行的服務(wù)執(zhí)行通道,每個通道都在等待請求到來并提供服務(wù)掩幢。 在這種情況下,每個請求其實花費了 1 秒际邻。
現(xiàn)在我們知道,平均響應(yīng)時間其實應(yīng)該在每任務(wù) 0 秒到 1 秒之間世曾。 但是我們不能僅僅從吞吐量的測量數(shù)據(jù)中推導(dǎo)出平均響應(yīng)時間。(事實上存在數(shù)學(xué)模型從吞吐量推導(dǎo)出平均響應(yīng)時間肿轨,但這個模型要求更多的輸入?yún)?shù),而不僅僅是吞吐量) 你必須單獨測量它椒袍。
反過來說也是一樣的藻茂,你應(yīng)該能從我上面給出的例子中得到啟發(fā)驹暑。 下面是一個更有趣的例子辨赐。
例 2
你的客戶要求一個新任務(wù)必須滿足在單核 CPU 的計算機上達(dá)到每秒 100 的吞吐量。 假如這個新任務(wù)在客戶的系統(tǒng)上執(zhí)行一次需要 0.001 秒帆焕。 那么你的程序能夠滿足客戶要求的吞吐量么不恭?
你可能會說叶雹,跑一次這個任務(wù)只需要千分之一秒换吧,那么在一秒內(nèi)完成 100 次顯然是綽綽有余的。 恩满着,是的贯莺,你很正確风喇,假如這個任務(wù)被很好的串行化了乖篷。 例如透且,你的程序處理 100 個任務(wù)執(zhí)行請求是在一個循環(huán)中豁鲤,一個接一個的執(zhí)行鲸沮,那就是正確的。
但是如果這 100 個任務(wù)到達(dá)你的系統(tǒng)是完全隨機的來自 100 個不同的用戶讼溺,那該怎么辦呢?CPU 調(diào)度器和串行資源(Oracle 的閂和鎖炫狱,內(nèi)存可寫緩沖區(qū)訪問)這些糟糕的實際情況會嚴(yán)格限制你的并發(fā)吞吐量低于每秒 100剔猿。 最終,你可能會達(dá)到客戶的期望也可能達(dá)不到归敬。 你不能僅僅從響應(yīng)時間推導(dǎo)出吞吐量,你必須單獨測量吞吐量汪茧。
所以,響應(yīng)時間和吞吐量不是那么簡單的互為倒數(shù)關(guān)系呀舔。 你想要知道這兩個指標(biāo)扩灯,就必須一起測量它們别威。那么響應(yīng)時間和吞吐量到底哪一個更重要呢驴剔? 在一些場景下,說哪一個都是合理的豺妓。 但在大多數(shù)情況下布讹,兩者都同樣重要。 因為描验,對系統(tǒng)來說它的業(yè)務(wù)需求通常是這樣的,在大于 99% 的情況下響應(yīng)時間要少于 1 秒膘流,并且能支持 10 分鐘內(nèi)持續(xù)不低于 1000 的吞吐量鲁沥。
4. 百分比指標(biāo)
在上一節(jié)耕魄,我用了“大于 99%”這樣的描述來表達(dá)對響應(yīng)時間的期望。 但大部分人可能更習(xí)慣于這樣的描述:“平均響應(yīng)時間少于 r 秒”允扇。 但從經(jīng)驗的角度则奥,使用百分比方式更好考润。
例 3
假想每天運行在你的計算機上的任務(wù)的響應(yīng)時間的容忍極限是 1 秒读处。進(jìn)一步假設(shè)「表1」列出了該任務(wù)執(zhí)行 10 次的測量值。 這兩個列表的平均響應(yīng)時間都是 1 秒俊戳。哪一個你認(rèn)為更好馆匿?
雖然你看到兩個列表擁有同樣的平均響應(yīng)時間,但本質(zhì)上差別很大渐北。ListA 90% 的響應(yīng)時間是低于 1 秒的,而 ListB 只有 60% 的時間是低于 1 秒的赃蛛。從用戶體驗的角度來說,ListB 表明會有 40% 的用戶會感到不滿意破托,而 ListA 僅有 10% 的不滿意率歧蒋,雖然它們平均響應(yīng)時間相同。
ListA 90% 的響應(yīng)時間是 0.987 秒谜洽,而 ListB 90% 的響應(yīng)時間是 1.273 秒。 因此使用百分比描述的響應(yīng)時間比平均響應(yīng)時間包含更多的信息量序臂。
正如 GE 公司所說:“客戶感受到的是差異變化,而非平均”奥秆。(參見GE的《什么是六西格瑪》) 可見使用百分比來描述響應(yīng)時間更符合終端用戶的期望:例如,99.9% 的跟蹤貨運單的任務(wù)必須在 0.5 秒內(nèi)完成吭练。
5. 問題診斷
最近我被邀請去解決的一些性能問題的描述都是些關(guān)于響應(yīng)時間的析显。 如:“過去只用不到 1 秒的時間就能完成 X 任務(wù),但是現(xiàn)在卻需要 20 秒分尸〈踵冢” 當(dāng)然,一些真正的問題隱藏在其他一些問題描述的表象背后尺上,例如:“我們的系統(tǒng)變的很慢,完全沒法用了怎抛。”
雖然我經(jīng)常碰到類似這樣的表述豆赏,但并不意味著你應(yīng)該這樣去描述問題富稻。 首先你得清晰得描述問題本身,才可能把它們弄清楚椭赋。 一個好辦法是去詢問,你想要達(dá)到得目標(biāo)狀態(tài)是怎樣的呢苟跪? 找到一些細(xì)節(jié),你可以用量化的方式來表達(dá)它們件已。 例如:執(zhí)行 X 任務(wù)大部分情況下都超過 20 秒元暴,希望能在 95% 的情況下小于 1 秒鉴未。
理論上這聽起來很棒,但要是你的用戶根本沒有很具體的可以量化的目標(biāo)呢?或者你的用戶根本就不知道怎樣去量化,更糟糕的情況是你的用戶如果還有一些完全不切實際的期望怎么辦?你如何知道到底什么是“可能的”,什么是“不切實際的”绞绒?
好吧拐揭,下面我們繼續(xù)探討這些問題家肯。
6. 序列圖
序列圖是一種 UML(統(tǒng)一建模語言)中定義的圖形種類式镐,用于表達(dá)對象間交互的發(fā)生順序。序列圖特別適合用于可視化的表達(dá)響應(yīng)時間燎孟。 在「圖1」中,我們展示了一個由瀏覽器、應(yīng)用服務(wù)器和數(shù)據(jù)庫構(gòu)成的簡單應(yīng)用系統(tǒng)的序列圖尘喝。
假如我們擴展下序列圖的表示缔赠,讓請求和響應(yīng)之間距離表示服務(wù)該請求的消耗時長。 在「圖2」中我展示了一個擴展后的序列圖告匠。
通過「圖2」你可以很直觀的看到到底是哪個部分消耗了最多的時間输莺。你能直觀的感受到整個響應(yīng)時間在各個部分的構(gòu)成。序列圖很好的幫助人們從概念上直觀的理解一個任務(wù)如何在系統(tǒng)各個部分之間順序流轉(zhuǎn)的甘畅。序列圖也能很好的表達(dá)并行執(zhí)行的任務(wù)。序列圖也是一個很棒的工具用于在業(yè)務(wù)層次分析性能問題准给。
序列圖是很好的描述性能問題的概念工具,但要把性能問題分析清楚我們還需要其他的。序列圖的問題是,假設(shè)有個任務(wù)花費了 2468 秒才執(zhí)行完成(大約 41 分 8 秒)畴蒲。 在這 41 分鐘里掩宜,應(yīng)用服務(wù)器和數(shù)據(jù)庫大約交互了 322968 次慧瘤。 把這個過程畫成序列圖大概就是下面「圖3」的樣子:
在應(yīng)用服務(wù)器和數(shù)據(jù)庫之間有如此之多的箭頭桦沉,以至于你完全看不清細(xì)節(jié)了纯露。我們可能需要花費數(shù)周才能畫完這個圖代芜,但這并不是一個有效的方法。序列圖雖然很好的概念可視化了任務(wù)的執(zhí)行流和時間流挤庇,但要仔細(xì)分析清楚響應(yīng)時間的問題我們還需要別的工具。
7. 性能剖析
對于像上述這種擁有大量調(diào)用交互的情況渴语,序列圖不能很好的描述。我們需要一種更方便的聚合視圖來更容易的理解到底哪個部分消耗了最多的時間驾凶。 「表2」給出了一個性能剖析的例子潮改。性能剖析是對響應(yīng)時間的表格化分解腹暖,按響應(yīng)時長倒序排列如下。
例 4
「表2」中的性能剖析是很初級的脏答,但它能告訴你最慢的 8 個任務(wù)占用了 2468 秒。從中你大概可以得到每個函數(shù)的響應(yīng)時長占比阿蝶。也可以從中算出每個函數(shù)調(diào)用的平均響應(yīng)時間黄绩。
性能剖析指出了哪些代碼花費了你的時間,也許更重要的是告訴你哪些代碼并沒有花費太多時間爽丹。當(dāng)你不得不去猜測代碼的性能瓶頸時,性能剖析是有巨大價值的粤蝎。
從「表2」的數(shù)據(jù)表明,大約 70.8% 的響應(yīng)時間消耗在了 DB:Fetch()
這個方法上秸应。如果你進(jìn)一步深入方法調(diào)用中會發(fā)現(xiàn) App:await_db_netIO()
方法與 DB:Fetch()
的一一對應(yīng)關(guān)系。于是能知道每個部分消耗了多少時間软啼,通過性能剖析你開始能夠明確的回答像這樣的問題:“這個任務(wù)需要運行多長時間?”從第 5 節(jié)你可以知道霉囚,對問題診斷的第一步來說這是一個很重要的問題匕积。
8. 阿姆達(dá)爾定律
性能剖析能幫你分析清楚性能問題。即便吉恩·阿姆達(dá)爾(Gene Amdahl)在 1967 年沒有告訴我們阿姆達(dá)爾定律闪唆,你也可以在看到性能剖析表格時自己歸納出來。阿姆達(dá)爾定律指出:
系統(tǒng)中對某一部件采用更快執(zhí)行方式所能獲得的系統(tǒng)性能改進(jìn)程度票顾,取決于這種執(zhí)行方式被使用的頻率帆调,或所占總執(zhí)行時間的比例。
所以如果你嘗試改進(jìn)的部分只占總響應(yīng)間的 5%番刊,那么對總響應(yīng)時間的提高最多不會超過 5%。這意味著你改進(jìn)的部分在性能剖析列表中排位越高(假設(shè)它們按倒序排列)芹务,你獲得的收益就越大。
但這并不意味著你一定要按照性能剖析列表的順序從高到低進(jìn)行改進(jìn)熔吗,這里你還需要考慮改進(jìn)的成本問題佳晶。
例 5
看下「表3」,它基本和「表2」一樣〗窝恚「表3」額外給出了你實施最好的改進(jìn)方案所能達(dá)到的效果以及相應(yīng)的實施成本。
那么你應(yīng)該先實現(xiàn)哪一項改進(jìn)呢晒他?阿姆達(dá)爾定律告訴我們改進(jìn)第一項的潛在收益最大逸贾,大約可以減少851秒(34.5% * 2468秒)津滞。但改進(jìn)第一項真的非常昂貴灼伤,那么改進(jìn)第二項也許能產(chǎn)生更多的凈收益。這才是我們真正需要改進(jìn)的瓶頸所在狐赡,盡管它僅能節(jié)省大約 303 秒。
性能剖析的巨大價值在于你能夠確切的了解你預(yù)期的投資能獲得多大的改進(jìn)鸟雏,它為你的改進(jìn)實施方案提供了決策支持,為你在預(yù)測給性能問題的度量時提供了參照孝鹊。當(dāng)你能夠找到一種比預(yù)期成本更低展蒂,減少響應(yīng)時間比預(yù)期更多的改進(jìn)方式時,這給你了一個很好展示聰明才智的機會锰悼。
你首先實施哪一項改進(jìn),歸結(jié)于你對成本評估有多大把握耐薯。簡單省事的改進(jìn)的措施是否考慮了改進(jìn)可能造成的系統(tǒng)風(fēng)險隘世?一個很簡單的改進(jìn),例如調(diào)整了某個參數(shù)丙者,取消了一個索引可能會潛在的破壞了一些目前性能表現(xiàn)良好的功能营密,而你又完全沒考慮倒。靠譜的成本評估則是展現(xiàn)你技術(shù)能力的另一個領(lǐng)域了纷捞。
另一個因素值得考慮的是被去,你可以通過一些小的成功來積累政治資本主儡。也許一些便宜低風(fēng)險的改進(jìn)并不能帶來響應(yīng)時間的大幅度降低惨缆,但可以通過跟蹤記錄這些小改進(jìn)來印證你對響應(yīng)時間提升的預(yù)測丰捷。在神話和迷信統(tǒng)治了數(shù)十年的軟件性能領(lǐng)域寂汇,這些對性能的預(yù)測和印證的跟蹤記錄,可以影響你的同事(工程師停巷、經(jīng)理榕栏、客戶)并建立自己的信譽,然后你才可能實施更昂貴的改進(jìn)方案扒磁。
最后提醒一句:當(dāng)你不斷取得勝利并建議實施成本更高、風(fēng)險更大的改進(jìn)措施時渗磅,可千萬別掉以輕心。信任是很脆弱的仔掸,你做了很多事情才取得信任医清,但可能只是因為一次粗心大意的錯誤就會摧毀它。
9. 偏斜度
當(dāng)你使用性能剖析時会烙,你會反復(fù)遇到類似這樣的衍生問題。
例 6
從「表2」中可以看到一共調(diào)用了 322,968 次 DB:fetch()
方法纸厉,花費了 1748.229 秒五嫂。假如我們將調(diào)用量降低一半,那么響應(yīng)時間會降低多少沃缘?答案絕對不會是降低一半,花點時間思考下面這個更簡單點的例子槐臀。
例 7
調(diào)用 4 個方法花費了 4 秒鐘,那么減少為調(diào)用 2 個方法花費多少時間得糜?答案依賴于我們省掉的調(diào)用到底是哪些方法。你可能這樣假設(shè)了掀亩,每個方法的平均調(diào)用時間就是 4 / 4 = 1 秒。但我可沒在問題描述中告訴你每個方法的調(diào)用耗時是一樣的捉蚤。
例 8
假設(shè)下面兩種可能性炼七,每個列表列出了 4 個方法調(diào)用的響應(yīng)時間
A = {1, 1, 1, 1}
B = {3.7, .1, .1, .1}
在 A 中響應(yīng)時間是一致的,所以無論我們省掉了哪兩個調(diào)用豌拙,最后響應(yīng)時間都能縮短到 2 秒。但在 B 中捉超,到底省掉哪兩個方法調(diào)用對響應(yīng)時間的影響是有很大差別的唯绍。如果我們?nèi)サ纛^兩個調(diào)用,響應(yīng)時間縮短為 0.2 秒况芒,提升了 95%。但如果我們?nèi)サ舻氖呛髢蓚€調(diào)用耐版,響應(yīng)時間變?yōu)?3.8 秒压汪,僅僅提升了 5%。
偏斜度表達(dá)在一組值中的非一致性程度蛾魄。正是因為偏斜度的存在,所以你沒法準(zhǔn)確的回答我在本節(jié)開頭的問題。 讓我們再回頭看看這個例子:
例 9
在不知道偏斜度的前提下叽奥,你只能回答響應(yīng)時間可能減少的范圍是在 0 到 1748.229 秒之間,這也是你能提供的最好的回答了魔市。
盡管如此,假設(shè)你有一些額外的信息待德,如「表4」所示,你就能對最好和最壞的情況進(jìn)行估算绘闷。進(jìn)一步說,假如你有了「表4」中信息就會很聰明的去特別優(yōu)化響應(yīng)時間在 0.01 秒到 0.1秒 之間的那 47,444 個調(diào)用印蔗。
10. 最小化風(fēng)險
前面的章節(jié)我提到過丑勤,當(dāng)修復(fù)一個任務(wù)性能問題時可能破壞另一個任務(wù)的性能,讓我想起了一件曾經(jīng)在丹麥發(fā)生的事耙厚。這個故事很短:
場景
在丹麥的巴勒魯普自治市(M?l?v)的一張橡木餐桌前岔霸,大約 10 個人圍坐一起,在用筆記本工作和相互交流秉剑。
Cary: 伙計們我快熱死了,你們不介意我打開窗戶放點冷空氣進(jìn)來吧诡曙?
Carel-jan: 為什么你不把你的厚毛衣脫了呢略水?
完。
在這里渊涝,有個樂觀的人都知道的一般性原則在發(fā)揮效力:當(dāng)大家都很舒適除了你以外,那么你首先應(yīng)該確保影響自己的東西是否正常跨释,否則你可能去搞亂一些全局的東西導(dǎo)致每一個人都受影響。
正是這個原則岁疼,當(dāng)因為幾個寫的很爛的 Java 應(yīng)用程序有人建議我去調(diào)整 Oracle 的網(wǎng)絡(luò)包大小時讓我感到很害怕缆娃。這些很爛的程序產(chǎn)生了很多不必要的數(shù)據(jù)庫調(diào)用瑰排,自然也產(chǎn)生了很多不必要的網(wǎng)絡(luò)等待暖侨。當(dāng)其他一切正常除了這幾個爛程序,那么最安全的做法是將調(diào)整的范圍本地化京郑,只需要去調(diào)整這幾個爛程序就好了。
11. 效率
即便依賴此系統(tǒng)進(jìn)行工作的所有人都很痛苦傻挂,你依然需要首先專注于業(yè)務(wù)上最優(yōu)先需要修正的程序部分挖息。讓程序工作的盡可能的高效是一個很好的切入點。在不增加容量套腹,不犧牲必須的業(yè)務(wù)功能的前提下,效率是能夠節(jié)省下來的任務(wù)總執(zhí)行時間的倒數(shù)电禀。
換句話說,效率就是從反面對浪費進(jìn)行的度量症副。下面是一些經(jīng)常發(fā)生在數(shù)據(jù)庫應(yīng)用中浪費的例子政基。
一個中間層程序為插入數(shù)據(jù)庫的每條記錄創(chuàng)建了一條獨立的 SQL 語句。它執(zhí)行了 10,000 次數(shù)據(jù)庫預(yù)編譯語句調(diào)用沮明,導(dǎo)致了 10,000 次網(wǎng)絡(luò) I/O 調(diào)用。其實它可以只使用一條預(yù)編譯語句荐健,從而節(jié)省 9,999 次網(wǎng)絡(luò) I/O 調(diào)用。
一個 SQL 語句訪問數(shù)據(jù)庫緩沖區(qū)緩存 7,428,322 次獲得了 698 行的結(jié)果集纺酸。使用一個額外的過濾預(yù)測址否,只返回了終端用戶真正想要看見的 7 行結(jié)果,只需訪問緩沖區(qū)緩存 52 次在张。
確實如果一個系統(tǒng)存在一些全局性的問題(不良索引、錯誤參數(shù)啄骇、弱弱的硬件配置)導(dǎo)致了一大片任務(wù)執(zhí)行的低效率瘟斜,你應(yīng)當(dāng)修正它。但不要嘗試調(diào)優(yōu)系統(tǒng)去滿足低效的程序螺句。有很多辦法來調(diào)優(yōu)低效的程序本身。即使這個程序是商業(yè)的現(xiàn)成的軟件芽唇,那么和你的軟件供應(yīng)商一起去優(yōu)化程序本身比你去優(yōu)化系統(tǒng)讓其盡可能的高效從長期來說會更受益取劫。
讓程序變的更高效會讓工作在這個系統(tǒng)上的每一個人都受益巨大。很容易看到浪費的減少對任務(wù)響應(yīng)時間的貢獻(xiàn)谱邪。但依然有很多人不明白為什么提升這部分程序的性能會導(dǎo)致一種副作用,讓看起來完全不相關(guān)的另一個程序性能變差咆课。
其實這是系統(tǒng)負(fù)載在作祟。
12. 負(fù)載
負(fù)載(Load)是并發(fā)任務(wù)執(zhí)行時引發(fā)的資源競爭书蚪。負(fù)載正是我們?yōu)槭裁床荒茉谛阅軠y試中捕捉到所有性能問題的原因蘸吓,而這些問題以后會在生產(chǎn)環(huán)境發(fā)生。負(fù)載的一個測量指標(biāo)是使用率箩艺,使用率反應(yīng)了資源按時間分片的使用情況宪萄。當(dāng)某個資源使用率上升時,那么請求該資源服務(wù)的用戶就不得不經(jīng)歷更長的響應(yīng)時間拜英。任何一個在城市的高峰期開車的人都經(jīng)歷過類似現(xiàn)象。當(dāng)交通變的嚴(yán)重?fù)矶聲r虫给,你不得不在收費站前等待更長的時間。
你的汽車在開闊的道路上能開上每小時 60 英里抹估,但在擁堵的路上只能以每小時 30 英里的速度行駛,而軟件不會像汽車這樣真的變慢瓷式。軟件按照固定的同樣的速度執(zhí)行,每個時鐘周期總是執(zhí)行同樣數(shù)量的指令贸典,但響應(yīng)時間會隨著系統(tǒng)資源變的繁忙而嚴(yán)重退化踱卵。
負(fù)載上升系統(tǒng)變慢的原因有兩個:隊列延遲 和 相關(guān)性延遲。下面我會進(jìn)一步講述颊埃。
13. 隊列延遲
負(fù)載和響應(yīng)時間之間在數(shù)學(xué)上的相關(guān)性大家都很熟悉了。一個叫做「M/M/m」的數(shù)學(xué)模型(譯注:「M/M/m」是一個關(guān)于隊列理論的數(shù)學(xué)模型饥漫,你無需詳細(xì)搞明白也能從感性認(rèn)識并理解作者的分析罗标。)將響應(yīng)時間和負(fù)載關(guān)聯(lián)起來應(yīng)用于一些特定的需求場景下〕瓜「M/M/m」模型的一個假設(shè)前提是你的系統(tǒng)模型擁有理論上的完美擴展性。這個假設(shè)非常類似于我們在初級物理學(xué)課程中經(jīng)常提到的光滑表面(無摩擦力)假設(shè)宾尚。
雖然「M/M/m」模型假設(shè)的條件有些不現(xiàn)實谢澈,如完美的可擴展性,但從中依然可以學(xué)到很多锥忿。「圖4」使用「M/M/m」模型展示了負(fù)載和響應(yīng)時間之間的關(guān)系淹朋。
從「圖4」,你從數(shù)學(xué)的角度看到了系統(tǒng)在不同負(fù)載條件下給你的感受础芍。低負(fù)載下的響應(yīng)時間和無負(fù)載基本一樣者甲。當(dāng)負(fù)載上升時虏缸,你能感受到響應(yīng)時間有一個輕微刽辙、平緩的退化宰缤。這種平緩的變化不會造成什么麻煩晃洒,但隨著負(fù)載繼續(xù)上升響應(yīng)時間開始以一種急劇的方式退化氧骤,這可要造成大麻煩了吃引。
響應(yīng)時間在具備完美擴展性的「M/M/m」模型下由兩個部分組成:服務(wù)時間 和 隊列延遲筹陵。
就是這樣一個等式:R = S + Q
服務(wù)時間(S)就是任務(wù)的執(zhí)行時間朦佩。
隊列延遲(Q)就是任務(wù)在隊列中等待機會獲得消費某個資源的時間庐氮。
所以當(dāng)你在 Taco Tico(美國和墨西哥邊境的快餐連鎖)訂餐時弄砍,你的訂單響應(yīng)時間(R)就包括了等待服務(wù)員來餐桌邊接收訂單的等待時間输枯,這就是隊列延遲等待(Q)桃熄,而服務(wù)時間(S)就是從訂單交到服務(wù)員時到食物送到你手上的等待時間。 同樣碉京,任務(wù)的響應(yīng)時間在有負(fù)載和無負(fù)載的系統(tǒng)之間是有差別的谐宙。
14. 拐點
提及性能凡蜻,你想要達(dá)到兩個目標(biāo):
- 你想要獲得最快的響應(yīng)時間:你不想任務(wù)的完成需要太長的時間兑巾。
- 你想要獲得最大的吞吐量:同一時間能支持更多人執(zhí)行他們的任務(wù)蒋歌。
不幸的是這兩個目標(biāo)是相互矛盾的堂油。優(yōu)化達(dá)到第一個目標(biāo)需要你最小化系統(tǒng)的負(fù)載府框,而達(dá)到第二個目標(biāo)則要最大化系統(tǒng)負(fù)載寓免,二者不可兼得袜香。 在這兩者之間的某個負(fù)載值就是系統(tǒng)的最優(yōu)負(fù)載蜈首。
處于最優(yōu)負(fù)載平衡點的資源使用率的值欢策,我稱其為「拐點」赏淌。系統(tǒng)中某種資源達(dá)到「拐點」后六水,那么吞吐量被最大化了而對響應(yīng)時間只有很小的負(fù)面影響。從數(shù)學(xué)上來講荣茫,「拐點」就是響應(yīng)時間除以資源利用率所得結(jié)果最小的值。 「拐點」有個很好的屬性,就是位于從原點畫一條直線正好與響應(yīng)時間曲線相切的位置咧欣。 在一個仔細(xì)繪制的「M/M/m」圖中该押,你能很容易的利用這個性質(zhì)找到「拐點」,如下「圖5」所示梢什。
關(guān)于「M/M/m」模型「拐點」的另一個很好的屬性是你只需要知道一個參數(shù)就可以計算出它。這個參數(shù)就是系統(tǒng)中并行的荔睹、相同的和獨立的服務(wù)通道數(shù)僻他。服務(wù)通道是一種資源吨拗,它們共享一個隊列劝篷,其他資源像收費站或者 SMP(Symmetric multiprocessing 對稱多處理)結(jié)構(gòu)的計算機中的 CPU 都是類似的概念娇妓。
在「M/M/m」模型中活鹰,斜體小寫的 m 表示系統(tǒng)建模時服務(wù)通道數(shù)。對任意一個系統(tǒng)來說仅乓,計算「拐點」都是很困難的,好在我已經(jīng)給你計算出來了豫喧〈逼茫「表5」中列出了一些常見的服務(wù)通道數(shù)的「拐點」值缕棵。(此時你也許想知道在「M/M/m」隊列模型中另外兩個 M 代表什么招驴。它們與請求進(jìn)入時刻和服務(wù)時間的隨機性假設(shè)有關(guān)虱饿。 更多請參考 Kendall's Notation 或進(jìn)一步參考 Optimizing Oracle Performance)
為何「拐點」如此重要氮发?對于那些請求隨機到達(dá)的系統(tǒng)爽冕,如果資源負(fù)載持續(xù)超過「拐點」扇售,那么響應(yīng)時間和吞吐會因為負(fù)載的輕微變化而嚴(yán)重波動承冰。 所以困乒,對于請求隨機到達(dá)的系統(tǒng)而言娜搂,保持負(fù)載低于拐點是至關(guān)重要的百宇。
(譯注:從上面「表5」可以看出携御,為什么經(jīng)驗值將 4 核的虛擬化容器 CPU 負(fù)載紅色報警點在 60%啄刹,32或64 核物理機的 CPU 負(fù)載紅色報警點在 80%誓军。)
15. 拐點的相關(guān)性
那么「拐點」的概念是不是真的如此重要呢昵时? 畢竟壹甥,我曾經(jīng)告訴過你「M/M/m」模型建立在一個理想的烏托邦理念之上,那就是系統(tǒng)擁有完美的可擴展性俄占。我知道你正在想什么:你想的都是錯的缸榄。
「M/M/m」模型告訴我們甚带,即便你的系統(tǒng)擁有完美的可擴展性鹰贵,你依然會遭遇巨大的性能問題只要系統(tǒng)的平均負(fù)載超過了圖表中給出的拐點碉输。那么現(xiàn)實中你的系統(tǒng)不可能比「M/M/m」假設(shè)的理論系統(tǒng)更完美敷钾。所以阻荒,你的系統(tǒng)的真實「拐點」會比我在「表5」中給出的更小侨赡。(我在這里對拐點使用了復(fù)數(shù)形式菜秦,因為你可以基于 CPU 來建立拐點模型球昨,同時也可以基于你的磁盤主慰、網(wǎng)絡(luò) I/O 等等共螺。)
再次說明:
- 你的系統(tǒng)中的每一項資源都有一個「拐點」藐不。
- 你的系統(tǒng)「拐點」都是小于或等于「表5」中給出的理論值雏蛮,你的系統(tǒng)擴展的完美性越差挑秉,「拐點」越小。
- 對于請求隨機到達(dá)的系統(tǒng)姻灶,如果資源負(fù)載持續(xù)超過「拐點」木蹬,你將遭遇性能問題镊叁。
所以晦譬,保持負(fù)載低于拐點是至關(guān)重要的敛腌。
(譯注:所以性能測試干的就是找出真實系統(tǒng)的負(fù)載拐點尤莺,并和理論值比較就可以看出系統(tǒng)的橫向擴展性是否有瓶頸點颤霎。)
16. 容量規(guī)劃
理解了「拐點」可以減少容量規(guī)劃的復(fù)雜性友酱,可以這樣來規(guī)劃:
- 某項資源的容量就是在高峰期能輕松的運行你的任務(wù)而資源使用率不會超過「拐點」缔杉。
- 保持資源利用率低于「拐點」或详,那么系統(tǒng)表現(xiàn)就基本不會給你帶來大的驚訝鸭叙。
- 但是,如果系統(tǒng)中任何一項資源超出了它們的「拐點」勋乾,你就會遭遇性能問題,無論你是否意識到各吨。
- 如果你遭遇性能問題揭蜒,不要糾結(jié)于數(shù)學(xué)模型上屉更,要修正這些問題要么重新安排下負(fù)載分配欺冀,要么減少負(fù)載隐轩,要么增加容量职车。
這就是將性能管理過程和容量規(guī)劃結(jié)合起來的辦法。
17. 隨機到達(dá)
你可能已經(jīng)注意到了称勋,我在前文經(jīng)常提及“隨機到達(dá)”這個說法赡鲜,為什么它如此重要银酬?現(xiàn)在一些系統(tǒng)擁有的特征你可能不會具備,如:完全確定的作業(yè)調(diào)度李破。另外一些系統(tǒng)被配置為接受任務(wù)的方式像是機器人模式嗤攻,如每秒接受一個任務(wù),十分固定闯团,當(dāng)然現(xiàn)在這些系統(tǒng)很少見了偷俭。我這里說的一秒一個任務(wù)淹遵,并不是說平均一秒一個任務(wù)透揣,例如第一秒 2 個任務(wù)辐真,而下一秒 0 個任務(wù)。我指的是均勻的一秒來一個任務(wù)楔脯,類似汽車工廠組裝線上機器人的工作模式。
如果任務(wù)到達(dá)系統(tǒng)是完全確定的木柬,就是說你完全能預(yù)知下一個請求什么時候到達(dá)眉枕,那么讓資源的使用率超過「拐點」必然不會引發(fā)性能問題。對于一個任務(wù)到達(dá)很確定的系統(tǒng),那么你的目標(biāo)應(yīng)該是將資源利用到 100%想许,而不是讓它們排隊等待糜烹。
「拐點」對于隨機到達(dá)的系統(tǒng)如此重要的原因是疮蹦,隨機任務(wù)請求往往會聚集并引發(fā)短暫的資源使用脈沖式上升。這些脈沖式上升需要足夠的剩余容量來消化它們感论,所以當(dāng)脈沖發(fā)生時可能就會引發(fā)隊列延遲并導(dǎo)致響應(yīng)時間的明顯起伏比肄。
短暫的脈沖并導(dǎo)致資源使用率超過「拐點」也還好,只要不要持續(xù)達(dá)到數(shù)秒時間妥色。這個數(shù)秒到底應(yīng)該是多少秒呢? 我相信(當(dāng)然我沒試過去證明)這個時間最好不要超過 8 秒吼拥。(來自著名的互聯(lián)網(wǎng) 8 秒原則) 如果你無法滿足在特定百分比下響應(yīng)時間和吞吐量對用戶的承諾凿可,那么很顯然系統(tǒng)脈沖上升持續(xù)時間太長了枯跑。
18. 相關(guān)性延遲
你的系統(tǒng)肯定不具備理論上的完美擴展性。盡管我從沒分析過你的系統(tǒng)白热,但我敢打賭無論你的系統(tǒng)無論是什么樣的也不具備「M/M/m」理論模型假設(shè)的完美擴展性敛助,而相關(guān)性延遲正是你的建模不可能完美的原因。執(zhí)行任務(wù)時花在對共享資源訪問的協(xié)商和通信的時間就是相關(guān)性延遲屋确。和響應(yīng)時間纳击、服務(wù)時間、隊列延遲一樣焕数,相關(guān)性延遲也可以在任務(wù)的執(zhí)行中被測量纱昧,例如每點擊秒數(shù)。
這里我并不想描述預(yù)測相關(guān)性延遲的數(shù)學(xué)模型堡赔,但如果你分析過你的任務(wù)執(zhí)行情況识脆,你可以了解什么時候相關(guān)性延遲會發(fā)生。在 Oracle 中善已,一些同步的事件正是相關(guān)性延遲的例子:
- 入隊列(enqueue)
- 緩沖忙等待(buffer busy waits)
- 閂鎖釋放(latch free)
你不能使用「M/M/m」來對相關(guān)性延遲進(jìn)行建模灼捂,因為「M/M/m」模型假設(shè)了你的 m 個服務(wù)通道是完全并行的、等同的和獨立的雕拼。這個模型假設(shè)在一個先進(jìn)先出(FIFO)隊列中纵东,只要你等待的時間足夠長,在你之前的請求已出隊列并得到服務(wù)啥寇,那么最終你也會得到服務(wù)偎球,但是相關(guān)性延遲不是這樣工作的。
例 10
假設(shè)在一個 HTML 數(shù)據(jù)表單上辑甜,有個按鈕是「更新」衰絮,點擊它會執(zhí)行一條 SQL 更新語句。另外一個按鈕是「保存」磷醋,點擊它執(zhí)行事務(wù)提交將剛才的更新保存下來猫牡。如果一個應(yīng)用是這樣做的,我可以保證它的性能是非常糟糕的邓线。這是因為這樣一種設(shè)計淌友,讓下面的場景成為可能的,實際上這也是必然可能的骇陈。一個用戶先點擊了「更新」震庭,發(fā)現(xiàn)到了午餐時間,然后就去吃飯了你雌,過了兩小時下午回來再點擊「保存」器联。
對于想要更新同一行的其他任務(wù)來說,這是一個災(zāi)難婿崭。其他任務(wù)不得不等待獲取行鎖拨拓,更糟的情況下甚至是頁鎖,直到原來鎖定的用戶想起繼續(xù)點擊「保存」氓栈≡祝或者 DBA 來殺掉原來鎖定用戶的會話,這樣的話當(dāng)然又會給原用戶造成錯覺授瘦,他以為他更新了一行實際卻沒有醋界,這可糟透了祟身。
在這個例子中,不管系統(tǒng)繁忙與否物独,一個任務(wù)就在那無所事事的等待鎖的釋放。它依賴了系統(tǒng)資源利用率之外的一些隨機性因素氯葬。這就是為什么你不能使用「M/M/m」模型來對其進(jìn)行建模挡篓。這也是為什么在一個單元測試環(huán)境下的性能測試結(jié)果不足以用來決策是否應(yīng)該在生產(chǎn)環(huán)境添加一些新的代碼。
19. 性能測試
我們談到的隊列延遲帚称、相關(guān)性延遲引發(fā)了一個很困難的問題官研。你如何對一個新的應(yīng)用進(jìn)行足夠的測試,讓你信心滿滿的認(rèn)為它不為因為性能問題而對你的生產(chǎn)程序造成破壞闯睹。你可以去建模戏羽,也可以去測試。但是楼吃,在你真正遭遇這些問題之前始花,為所有你可以預(yù)見的生產(chǎn)問題去建立模型和測試是極其困難的。
因此孩锡,一些人看到了這樣窘境酷宵,因此申辯說那么就干脆別測試了。千萬別被這樣的心態(tài)所困擾躬窜。下面的觀點是很確定的:
- 在程序進(jìn)入生產(chǎn)環(huán)境之前浇垦,如果你嘗試去發(fā)現(xiàn)一些問題你肯定會比那些完全不去做的找到更多的問題。
- 在預(yù)發(fā)布的測試中荣挨,你不可能發(fā)現(xiàn)所有的問題男韧,所以你需要一些可靠并有效的方法來解決這些在預(yù)發(fā)布測試中漏掉的問題。
在完全不測試和完整的生產(chǎn)環(huán)境模擬測試之間默垄,存在一個適度測試量的平衡點此虑。 當(dāng)然對于一家飛機制造商來說,適度測試量肯定多于一家銷售棒球帽的公司厕倍。但千萬別完全跳過性能測試寡壮。至少,當(dāng)你在生產(chǎn)環(huán)境遭遇不可避免的性能問題時讹弯,一份性能測試計劃將使你成為一名更稱職的診斷專家(更清晰的思考者)况既。
20. 測量
人們能感知到的就是吞吐量和響應(yīng)時間。吞吐量很容易測量组民,相對來說測量響應(yīng)時間要稍微困難些棒仍。(還記得吧,吞吐量和響應(yīng)時間可不是簡單的倒數(shù)關(guān)系)用個秒表來計時終端用戶的行為并不困難臭胜,但你不會從中得到你真正想要的關(guān)于為什么響應(yīng)時間如此之大的細(xì)節(jié)莫其。
不幸的是癞尚,人們總是測量他們?nèi)菀诇y量的,而不是他們應(yīng)當(dāng)測量的乱陡。 當(dāng)我們需要測量的東西不容易測量時浇揩,我們就把注意力轉(zhuǎn)移到那些容易得到測量數(shù)據(jù)上了,這是個錯誤憨颠。那些并不是你真正需要的測量胳徽,但看起來似乎和你真正需要的有些相關(guān)又容易去執(zhí)行的測量,我們稱之為「替代指標(biāo)」爽彤。 一些「替代指標(biāo)」例子包括像子程序調(diào)用計數(shù)和子程序執(zhí)行耗時的采樣數(shù)據(jù)养盗。對于「替代指標(biāo)」,很遺憾在我的母語中沒有更好的語句來表達(dá)我的想法适篙,但有一個大家都熟悉的現(xiàn)代表達(dá)方式:替代指標(biāo)真是惡心往核。(Surrogate measures suck.)
不幸的是,「惡心」在這里并不表示它沒用嚷节。要是替代指標(biāo)真的沒用就好了聂儒,那就沒人會使用它們了。問題就在于替代指標(biāo)有時是有用的丹喻,這讓使用替代指標(biāo)的人相信它們總是有用的薄货,但實際并不是這樣。
替代指標(biāo)有兩個嚴(yán)重的問題:
- 它們可能在系統(tǒng)不正常時告訴你系統(tǒng)一切正常碍论,這在統(tǒng)計學(xué)上叫做第一型錯誤谅猾,假陽性。
- 它們也可能在系統(tǒng)正常時告訴你系統(tǒng)出問題了鳍悠,這在統(tǒng)計學(xué)上叫做第二型錯誤税娜,假陰性。
這兩類錯誤浪費了人們許多的時間藏研。
當(dāng)你去評測一個真實系統(tǒng)方方面面敬矩,你能否取得成功在于你能從那個系統(tǒng)中獲得多少有效的測量數(shù)據(jù)。我曾有幸在 Oracle 的市場部門工作過蠢挡,那時許多軟件供應(yīng)商圍繞著我們積極的參與弧岳,這才使得正確的測量系統(tǒng)成為可能。讓軟件開發(fā)者使用 Oracle 提供的工具是另外一回事了业踏,至少我們的產(chǎn)品中具備這樣的能力(記錄有效的測量數(shù)據(jù))禽炬。
21. 性能是一項功能特性
性能是軟件程序的一項功能特性,就像你在 Bug 跟蹤系統(tǒng)中很方便的將「Case 1234」這樣一個字符串自動鏈接到編號 1234 的 Bug 案例上勤家。性能像所有其他軟件功能一樣腹尖,不是湊巧得到的,你需要去設(shè)計和構(gòu)建它伐脖。要想獲得好的性能热幔,你不得不去仔細(xì)的思考乐设、研究、學(xué)習(xí)绎巨,寫出額外的代碼來測試和支持它近尚。
盡管如此,像所有其他功能特性一樣场勤,在項目初期你還在調(diào)研肿男、設(shè)計和編寫代碼時你不可能知道性能到底會怎樣。對大多數(shù)應(yīng)用(可能是絕大多數(shù)却嗡,這個說法可能有爭議)而言性能都是未知的,直到它們投入實際使用階段嘹承。那么留給你的問題就是:因為在上線前你不可能知道你的應(yīng)用性能表現(xiàn)到底怎樣窗价,因此你需要在編寫應(yīng)用時考慮怎樣很容易的在生產(chǎn)環(huán)境修復(fù)性能問題。
正如大衛(wèi)·加文(David Garvin)告訴我們的叹卷,容易測量的東西也更容易管理(來自《建立一個學(xué)習(xí)型組織》1993年發(fā)表于《哈佛商業(yè)評論》) 那么要寫一個在生產(chǎn)環(huán)境容易修復(fù)問題的應(yīng)用程序撼港,首先要做的就是要容易在生產(chǎn)環(huán)境進(jìn)行測量。大多數(shù)時候骤竹,當(dāng)我提到生產(chǎn)環(huán)境的性能測量時人們就會陷入一種焦慮狀態(tài)帝牡,他們很擔(dān)心性能測量帶來的入侵效應(yīng)。他們立刻對采集哪些數(shù)據(jù)做出了妥協(xié)蒙揣,只留下那些「替代指標(biāo)」(更容易采集的)在數(shù)據(jù)采集表上靶溜,擁有額外數(shù)據(jù)采集代碼的軟件會變的比沒有這些代碼的更慢么?
我喜歡湯姆·凱特(Tom Kyte)以前對這個問題的回答懒震。他估計額外的性能測量代碼給 Oracle 帶來不超過 10% 性能損失罩息。他接著向那些氣惱的提問者作出解釋,正是因為從這些性能測量代碼獲取的數(shù)據(jù)讓 Oracle 公司進(jìn)一步將產(chǎn)品性能改進(jìn)提升了不止 10%个扰,這超出了性能測量代碼本身引發(fā)的開銷瓷炮。
我認(rèn)為很多軟件供應(yīng)商他們通常花費了太多時間來優(yōu)化他們的性能測量代碼路徑使其更高效递宅,而不是首先搞清楚怎么讓這些代碼有效果娘香。 高德鈉(Donald Knuth)曾在 1974 說過的一句話印證了這個觀點:
過早優(yōu)化是一切罪惡的根源。
軟件設(shè)計者將性能測量代碼整合進(jìn)他們的產(chǎn)品中更有可能創(chuàng)建一個高性能的應(yīng)用办龄,更重要的是這個應(yīng)用會不斷變的更快烘绽。
尾聲:關(guān)于「拐點」的公開辯論
在本文的 14 到 16 節(jié),我描述了「拐點」的性能曲線土榴、它們的相關(guān)性和應(yīng)用诀姚。但是,在 20 年前有一場關(guān)于是否值得定義一個「拐點」概念的公開辯論玷禽。
歷史上的一個重要的觀點認(rèn)為我所描述的「拐點」并不是真正有意義的赫段。在 1988 年呀打,斯蒂芬·薩姆森(Stephen Samson)爭論說至少在「M/M/1」的排隊系統(tǒng)的性能曲線中并不存在「拐點」贬丛。 他寫道:“選擇一個具備指導(dǎo)意義的數(shù)字并不容易,經(jīng)驗法則還是最適用的恭应,在大多數(shù)情況下都不存在拐點,無論你多么希望找到一個耘眨〉ㄓ欤”
1999 年,溫水煮青蛙的故事啟發(fā)了我纯趋。這個故事是這樣的說的憎兽,當(dāng)你把一只青蛙扔進(jìn)煮沸的開水中,它會立刻跳出來吵冒。但假如你先把它放在冷水中并慢慢的加熱水溫,青蛙會安靜的呆在水里直到被煮熟了桦锄。對于資源使用率扎附,它就像是沸水,有一個清晰的「死亡區(qū)間」结耀。在這個區(qū)間值內(nèi)留夜,對于隨機到達(dá)的請求你的系統(tǒng)將不堪重負(fù)。那么「死亡區(qū)間」的邊界在哪里?如果你嘗試用程序來自動管理資源使用率黑毅,你就必須知道這個值嚼摩。
最近,我的朋友尼爾·岡瑟(Neil Gunther)跟我有一場私下的辯論。首先枕面,他認(rèn)為「死亡區(qū)間」這個術(shù)語使用在這里是完全錯誤的愿卒,因為在函數(shù)連續(xù)性的前提下使用「死亡區(qū)間」是錯誤的。 其次潮秘,他認(rèn)為對于「M/M/1」系統(tǒng)的「拐點」在 0.5 是過于浪費了琼开,你應(yīng)當(dāng)更多的利用好系統(tǒng)資源,它應(yīng)高于 0.5 的資源利用率枕荞。最后柜候,他認(rèn)為你對使用率的明確定義取決于實際的平均響應(yīng)時間相對你能忍受的平均響應(yīng)時間實際超出了多少。因此躏精,岡瑟認(rèn)為任何有用的使用率閾值的定義都來源于詢問人們自身的偏好渣刷,而非來自于數(shù)學(xué)。(圖A)
從「圖B」中矗烛,我們可以看出這個說法的問題所在飞主。 假設(shè),你對平均響應(yīng)時間的忍耐限度是 T
高诺,那么對應(yīng)的最大資源利用率是 ρT
。你會看到在 ρT
附近資源利用率一個微小的變化都會導(dǎo)致響應(yīng)時間巨大的波動碾篡。
我相信如我在第 4 節(jié)所寫的虱而,客戶感受到的是差異變化,而非平均开泽。 或許他們會說我們能夠接受平均響應(yīng)時間達(dá)到 T
牡拇,但我不相信人們能忍受因為系統(tǒng)平均負(fù)載發(fā)生了 1% 的變化導(dǎo)致平均響應(yīng)時間達(dá)到 1 分鐘,換句話說就是平均響應(yīng)時間翻了 10 倍穆律。我確實了解我在 14 節(jié)列出的「拐點」列表比很多人直覺上感受到地安全值更低一些惠呼,特別是對「低階」的系統(tǒng)如「M/M/1」而言。 盡管如此峦耘,但我相信避免因為資源使用率的微小變化引發(fā)響應(yīng)時間的過大波動剔蹋,這是極其重要的。
話雖如此辅髓,我也不知道該如何確切的定義「過大」這個詞泣崩。像響應(yīng)時間波動的忍耐度,不同的人有不同的底線洛口〗酶叮或許有一個起伏忍耐的因子適用于所有人。例如第焰,Apdex Standard Application Performance Index 假設(shè)了響應(yīng)時間 F
是 T
的 4 倍就會讓人們的態(tài)度從「滿意」變?yōu)椤讣灏尽埂?/p>
正如我在 16 節(jié)中描述的买优,「拐點」無論你怎么去定義或稱呼它,對于容量規(guī)劃過程來說都是一個十分重要的參數(shù)。并且我相信它對日常的系統(tǒng)負(fù)載管理也是一個重要參數(shù)杀赢,我會繼續(xù)保持研究烘跺。
關(guān)于作者
Cary Millsap 是一家致力于軟件性能優(yōu)化公司 Method R 的創(chuàng)始人和 CEO,是一位在 Oracle 全球社區(qū)著名的演講者葵陵、教育者液荸、顧問和作者。曾和 Jeff Holt 合著 Optimizing Oracle Performance 一書脱篙,更多詳細(xì)信息參見作者 LinkedIn 的介紹和個人博客娇钱。
參考
- CMG (Computer Measurement Group, a network of professionals who study these problems very, very seriously); http://www.cmg.org.
- Eight-second rule; http://en.wikipedia.org/wiki/Network_performance#8-second_rule.
- Garvin, D. 1993. Building a learning organization. Harvard Business Review (July).
- General Electric Company. What is Six Sigma? The roadmap to customer impact. http://www.ge.com/sixsigma/SixSigma.pdf.
- Gunther, N. 1993. Universal Law of Computational Scalability; http://en.wikipedia.org/wiki/Neil_J._Gunther#Universal_Law_of_Computational_Scalability.
- Knuth, D. 1974. Structured programming with Go To statements. ACM Computing Surveys 6(4): 268.
- Kyte, T. 2009. A couple of links and an advert...; http://tkyte.blogspot.com/2009/02/couple-of-links-and-advert.html.
- Millsap, C. 2009. My whole system is slow. Now what? http://carymillsap.blogspot.com/2009/12/my-whole-system-is-slow-now-what.html.
- Millsap, C. 2009. On the importance of diagnosing before resolving. http://carymillsap.blogspot.com/2009/09/on-importance-of-diagnosing-before.html.
- Millsap, C. 2009. Performance optimization with Global Entry. Or not? http://carymillsap.blogspot.com/2009/11/performance-optimization-with-global.html.
- Millsap, C., Holt, J. 2003. Optimizing Oracle Performance. Sebastopol, CA: O'Reilly.
- Oak Table Network; http://www.oaktable.net.