認清性能問題

本文翻譯自 Thinking Clearly About Performance 這是我三年前讀到的一篇關於性能問題的好文,讀完後還覺不過癮,怕理解的不夠遂又翻譯了一遍,這也是當年個人第一次翻譯。html

這幾年來每次碰到性能問題,我都會想起這篇文章,它並不像不少其餘關於性能問題的文章,告訴你利用什麼工具怎麼去解決性能問題,這類文章更多屬於「術」的層面,而術的層面在不一樣的技術棧會有很不一樣的選擇。而本文則高屋建瓴的幫助讀者創建起對性能的正確認識,從而可以得到更全面的視角去看待和思考性能問題。這是「道」的層面,正所謂道法天然,術變萬千,深入理解了「道」,那麼面對性能問題的萬千之「術」纔不會那麼茫然。數據庫

文章略長,建議先收藏,稍後合適時抽出一塊時間來細細讀之,當有所獲。瀏覽器

摘要

對於開發者、技術管理者、架構師、系統分析師和項目經理來講,建立具有高性能特徵的複雜軟件都是一件極其困難的事。然而,經過了解一些基本原理,性能問題的解決和預防能夠更簡單可靠。本文講述了這些基本原理,涵蓋了一系列的目標、術語、工具和決策,綜合利用好它們來最大可能的建立一個長期有效的高性能應用。本文的一些例子來自於 Oracle 的經驗,但本文的範圍並不侷限於 Oracle 的產品。緩存

目錄

  • 公理化方法安全

  • 什麼是性能?性能優化

  • 響應時間 VS. 吞吐量服務器

  • 百分比指標微信

  • 問題診斷網絡

  • 序列圖架構

  • 性能剖析

  • 阿姆達爾定律

  • 偏斜度

  • 最小化風險

  • 效率

  • 負載

  • 隊列延遲

  • 拐點

  • 拐點的相關性

  • 容量規劃

  • 隨機到達

  • 相關性延遲

  • 性能測試

  • 測量

  • 性能是一項功能特性

  • 尾聲:關於「拐點」的公開辯論

  • 關於做者

  • 參考


1. 公理化方法

當我在 1989 年加入 Oracle 公司時,解決性能問題(人們一般說的是 Oracle 調優)是很困難的。 只有少部分人聲稱他們很擅長這個,不少人都去諮詢他們。 當時,我進到 Oracle 調優這個領域時,我徹底沒準備好。 最近我又開始對 MySQL 進行調優,這看起來和我 20 年前在 Oracle 公司作的差很少。

它讓我想起了當我 13 歲剛接觸代數學時是多麼的困難。 在那個年齡我只能依靠「數學直覺」來解決相似 3x + 4 = 13 這樣的方程。 問題是咱們之中大部分人都沒有所謂的「數學直覺」。 我記得當看到這樣的問題: 3x + 4 = 13 求解 x,只能採用試錯法偶然發現 x 應該是3。

試錯法給個人感受雖然能解決一些簡單的方程式,但很慢並且不爽。 一旦等式稍有變化如 3x + 4 = 14,試錯法就不能適應。 那麼該怎麼辦呢?當時我沒有好好思考過,直到 15 歲時 James R. Harkey 指引我走上正確的道路。

Harkey 先生教會我使用公理方法來解決代數方程問題。他給咱們展現了一系列的步驟(還給了我不少家庭做業進行練習)。作做業時除了記錄下這些步驟,還要寫下咱們是如何思考的。這樣咱們不只本身想得很清楚,並且經過一系列可靠的、可重複的步驟來向閱讀咱們做業的人證實了咱們確實搞明白了。 Harkey 先生看到的個人做業像下面這樣:

3.1x + 4 = 13               
待求解方程
3.1x + 4 - 4 = 13 - 4
減去相等的值
3.1x = 9
加法逆運算,化簡
3.1x ∕ 3.1 = 9 ∕ 3.1
除以相等的值
x ≈ 2.903
乘法逆運算,化簡求解

這就是 Harkey 先生教導的適用於代數學、幾何學、三角學和微積分的公理化方法。 由一系列符合邏輯的、可證實的和可審計的小步驟組成。這是我第一次真正從數學中學到的東西。

天然,當時我沒能認識到其中的價值,但證實做爲一種技能對我後來的成功相當重要。我發如今生活中,知道一件事很重要,但能向別人講清楚(證實)更重要。沒有好的證實技能,就很難成爲一名好的顧問、好的領導甚至好的員工。

我在上世紀 90 年代中期的目標是爲 Oracle 性能優化建立一套相似的、嚴格的公理化方法。後來我將其擴展到了 Oracle 以外,創建了一套適用於全部計算機軟件性能優化的公理化方法。好吧,我發現並不是全部人都喜歡這種說法,那咱們換一種說法:

咱們的目標就是幫助你想清楚如何優化你的軟件系統性能。

2. 什麼是性能?

假如你去 Google 下 Performance 這個關鍵字,可能會獲得 5 億個連接。 其中涉及的內容範圍可能從自行車比賽到可怕的員工審查流程(現在不少公司已經學會了避免這個流程)。但假如我去 Google 下 Performance 這個關鍵字,大部分的首頁連接都會與這篇文章的主題有關:__計算機軟件執行不管何種任務所花費的時間。__

任務這個詞是一個很適合的開始。任務是一個面向業務的工做單元。任務可以嵌套:打印發貨單是一個任務,打印一張發貨單(一個子任務)也是一個任務。當一個用戶提及性能時,他一般指的是系統執行一系列任務所花費的時間。__響應時間__ 是任務的執行時長,用每一個任務的時間來度量,像每點擊秒數。例如我用 Google 搜索關鍵字 Performance 的響應時間是 0.24 秒。 這個數據來自個人瀏覽器它渲染完 Google 網頁花費的時間,那麼很明顯,這量化了我對 Google 性能的直覺感知。

一些人對另一個性能指標很感興趣:吞吐量。 吞吐量 是在一個特定時間段內完成的任務的計數,例如:每秒點擊數。一般爲一羣人提供服務比爲個別人提供服務的人更關心吞吐量。例如,一個獨立會計會更關心日報的響應時間是否會致使今晚須要加班,而會計部的經理更關心繫統的是否能支撐全部的會計處理完今天的數據。

3. 響應時間 VS. 吞吐量

一般來說,響應時間和吞吐量是一個倒數關係(響應時間越長吞吐量越低),但這並不確切。 實際狀況更微妙、複雜一些。

例 1
假如,在一些性能基準測試中,你的系統的測量結果是每秒能處理 1000 個任務,那麼用戶的平均響應時間是多少? 你可能會說平均響應時間等於 1 / 1000 = 0.001 秒/每任務,但它真不是這樣的。

假如在你的系統內部擁有 1000 個相同的、獨立的、並行的服務執行通道,每一個通道都在等待請求到來並提供服務。 在這種狀況下,每一個請求其實花費了 1 秒。

如今咱們知道,平均響應時間其實應該在每任務 0 秒到 1 秒之間。 可是咱們不能僅僅從吞吐量的測量數據中推導出平均響應時間。(事實上存在數學模型從吞吐量推導出平均響應時間,但這個模型要求更多的輸入參數,而不只僅是吞吐量) 你必須單獨測量它。

反過來講也是同樣的,你應該能從我上面給出的例子中獲得啓發。 下面是一個更有趣的例子。

例 2
你的客戶要求一個新任務必須知足在單核 CPU 的計算機上達到每秒 100 的吞吐量。 假如這個新任務在客戶的系統上執行一次須要 0.001 秒。 那麼你的程序可以知足客戶要求的吞吐量麼?

你可能會說,跑一次這個任務只須要千分之一秒,那麼在一秒內完成 100 次顯然是綽綽有餘的。 恩,是的,你很正確,假如這個任務被很好的串行化了。 例如,你的程序處理 100 個任務執行請求是在一個循環中,一個接一個的執行,那就是正確的。

可是若是這 100 個任務到達你的系統是徹底隨機的來自 100 個不一樣的用戶,那該怎麼辦呢?CPU 調度器和串行資源(Oracle 的閂和鎖,內存可寫緩衝區訪問)這些糟糕的實際狀況會嚴格限制你的併發吞吐量低於每秒 100。 最終,你可能會達到客戶的指望也可能達不到。 你不能僅僅從響應時間推導出吞吐量,你必須單獨測量吞吐量。

因此,響應時間和吞吐量不是那麼簡單的互爲倒數關係。 你想要知道這兩個指標,就必須一塊兒測量它們。那麼響應時間和吞吐量到底哪個更重要呢? 在一些場景下,說哪個都是合理的。 但在大多數狀況下,二者都一樣重要。 由於,對系統來講它的業務需求一般是這樣的,在大於 99% 的狀況下響應時間要少於 1 秒,而且能支持 10 分鐘內持續不低於 1000 的吞吐量。

4. 百分比指標

在上一節,我用了「大於 99%」這樣的描述來表達對響應時間的指望。 但大部分人可能更習慣於這樣的描述:「平均響應時間少於 r 秒」。 但從經驗的角度,使用百分比方式更好。

例 3
假想天天運行在你的計算機上的任務的響應時間的容忍極限是 1 秒。進一步假設「表1」列出了該任務執行 10 次的測量值。 這兩個列表的平均響應時間都是 1 秒。哪個你認爲更好?

雖然你看到兩個列表擁有一樣的平均響應時間,但本質上差異很大。ListA 90% 的響應時間是低於 1 秒的,而 ListB 只有 60% 的時間是低於 1 秒的。從用戶體驗的角度來講,ListB 代表會有 40% 的用戶會感到不滿意,而 ListA 僅有 10% 的不滿意率,雖然它們平均響應時間相同。

ListA 90% 的響應時間是 0.987 秒,而 ListB 90% 的響應時間是 1.273 秒。 所以使用百分比描述的響應時間比平均響應時間包含更多的信息量。

正如 GE 公司所說:「客戶感覺到的是差別變化,而非平均」。(參見GE的《什麼是六西格瑪》) 可見使用百分比來描述響應時間更符合終端用戶的指望:例如,99.9% 的跟蹤貨運單的任務必須在 0.5 秒內完成。

5. 問題診斷

最近我被邀請去解決的一些性能問題的描述都是些關於響應時間的。 如:「過去只用不到 1 秒的時間就能完成 X 任務,可是如今卻須要 20 秒。」 固然,一些真正的問題隱藏在其餘一些問題描述的表象背後,例如:「咱們的系統變的很慢,徹底無法用了。」

雖然我常常碰到相似這樣的表述,但並不意味着你應該這樣去描述問題。 首先你得清晰得描述問題自己,纔可能把它們弄清楚。 一個好辦法是去詢問,你想要達到得目標狀態是怎樣的呢? 找到一些細節,你能夠用量化的方式來表達它們。 例如:執行 X 任務大部分狀況下都超過 20 秒,但願能在 95% 的狀況下小於 1 秒。

理論上這聽起來很棒,但要是你的用戶根本沒有很具體的能夠量化的目標呢?或者你的用戶根本就不知道怎樣去量化,更糟糕的狀況是你的用戶若是還有一些徹底不切實際的指望怎麼辦?你如何知道到底什麼是「可能的」,什麼是「不切實際的」?

好吧,下面咱們繼續探討這些問題。

6. 序列圖

序列圖是一種 UML(統一建模語言)中定義的圖形種類,用於表達對象間交互的發生順序。序列圖特別適合用於可視化的表達響應時間。 在「圖1」中,咱們展現了一個由瀏覽器、應用服務器和數據庫構成的簡單應用系統的序列圖。

假如咱們擴展下序列圖的表示,讓請求和響應之間距離表示服務該請求的消耗時長。 在「圖2」中我展現了一個擴展後的序列圖。

經過「圖2」你能夠很直觀的看到究竟是哪一個部分消耗了最多的時間。你能直觀的感覺到整個響應時間在各個部分的構成。序列圖很好的幫助人們從概念上直觀的理解一個任務如何在系統各個部分之間順序流轉的。序列圖也能很好的表達並行執行的任務。序列圖也是一個很棒的工具用於在業務層次分析性能問題。

序列圖是很好的描述性能問題的概念工具,但要把性能問題分析清楚咱們還須要其餘的。序列圖的問題是,假設有個任務花費了 2468 秒才執行完成(大約 41 分 8 秒)。 在這 41 分鐘裏,應用服務器和數據庫大約交互了 322968 次。 把這個過程畫成序列圖大概就是下面「圖3」的樣子:

在應用服務器和數據庫之間有如此之多的箭頭,以致於你徹底看不清細節了。咱們可能須要花費數週才能畫完這個圖,但這並非一個有效的方法。序列圖雖然很好的概念可視化了任務的執行流和時間流,但要仔細分析清楚響應時間的問題咱們還須要別的工具。

7. 性能剖析

對於像上述這種擁有大量調用交互的狀況,序列圖不能很好的描述。咱們須要一種更方便的聚合視圖來更容易的理解到底哪一個部分消耗了最多的時間。 「表2」給出了一個性能剖析的例子。性能剖析是對響應時間的表格化分解,按響應時長倒序排列以下。

例 4
「表2」中的性能剖析是很初級的,但它能告訴你最慢的 8 個任務佔用了 2468 秒。從中你大概能夠獲得每一個函數的響應時長佔比。也能夠從中算出每一個函數調用的平均響應時間。

性能剖析指出了哪些代碼花費了你的時間,也許更重要的是告訴你哪些代碼並無花費太多時間。當你不得不去猜想代碼的性能瓶頸時,性能剖析是有巨大價值的。

從「表2」的數據代表,大約 70.8% 的響應時間消耗在了 DB:Fetch() 這個方法上。若是你進一步深刻方法調用中會發現 App:await_db_netIO() 方法與 DB:Fetch() 的一一對應關係。因而能知道每一個部分消耗了多少時間,經過性能剖析你開始可以明確的回答像這樣的問題:「這個任務須要運行多長時間?」從第 5 節你能夠知道,對問題診斷的第一步來講這是一個很重要的問題。

8. 阿姆達爾定律

性能剖析能幫你分析清楚性能問題。即使吉恩·阿姆達爾(Gene Amdahl)在 1967 年沒有告訴咱們阿姆達爾定律,你也能夠在看到性能剖析表格時本身概括出來。阿姆達爾定律指出:

系統中對某一部件採用更快執行方式所能得到的系統性能改進程度,取決於這種執行方式被使用的頻率,或所佔總執行時間的比例。

因此若是你嘗試改進的部分只佔總響應間的 5%,那麼對總響應時間的提升最多不會超過 5%。這意味着你改進的部分在性能剖析列表中排位越高(假設它們按倒序排列),你得到的收益就越大。

但這並不意味着你必定要按照性能剖析列表的順序從高到低進行改進,這裏你還須要考慮改進的成本問題。

例 5
看下「表3」,它基本和「表2」同樣。「表3」額外給出了你實施最好的改進方案所能達到的效果以及相應的實施成本。

那麼你應該先實現哪一項改進呢?阿姆達爾定律告訴咱們改進第一項的潛在收益最大,大約能夠減小851秒(34.5% * 2468秒)。但改進第一項真的很是昂貴,那麼改進第二項也許能產生更多的淨收益。這纔是咱們真正須要改進的瓶頸所在,儘管它僅能節省大約 303 秒。

性能剖析的巨大價值在於你可以確切的瞭解你預期的投資能得到多大的改進,它爲你的改進實施方案提供了決策支持,爲你在預測給性能問題的度量時提供了參照。__當你可以找到一種比預期成本更低,減小響應時間比預期更多的改進方式時,這給你了一個很好展現聰明才智的機會。__

你首先實施哪一項改進,歸結於你對成本評估有多大把握。簡單省事的改進的措施是否考慮了改進可能形成的系統風險?一個很簡單的改進,例如調整了某個參數,取消了一個索引可能會潛在的破壞了一些目前性能表現良好的功能,而你又徹底沒考慮倒。__靠譜的成本評估則是展示你技術能力的另外一個領域了。__

另外一個因素值得考慮的是,你能夠經過一些小的成功來積累政治資本。也許一些便宜低風險的改進並不能帶來響應時間的大幅度下降,但能夠經過跟蹤記錄這些小改進來印證你對響應時間提高的預測。在神話和迷信統治了數十年的軟件性能領域,這些對性能的預測和印證的跟蹤記錄,能夠影響你的同事(工程師、經理、客戶)並創建本身的信譽,而後你纔可能實施更昂貴的改進方案。

最後提醒一句:當你不斷取得勝利並建議實施成本更高、風險更大的改進措施時,可千萬別掉以輕心。信任是很脆弱的,你作了不少事情才取得信任,但可能只是由於一次粗枝大葉的錯誤就會摧毀它。

9. 偏斜度

當你使用性能剖析時,你會反覆遇到相似這樣的衍生問題。

例 6
從「表2」中能夠看到一共調用了 322,968 次 DB:fetch() 方法,花費了 1748.229 秒。假如咱們將調用量下降一半,那麼響應時間會下降多少?答案絕對不會是下降一半,花點時間思考下面這個更簡單點的例子。

例 7
調用 4 個方法花費了 4 秒鐘,那麼減小爲調用 2 個方法花費多少時間?答案依賴於咱們省掉的調用究竟是哪些方法。你可能這樣假設了,每一個方法的平均調用時間就是 4 / 4 = 1 秒。但我可沒在問題描述中告訴你每一個方法的調用耗時是同樣的。

例 8
假設下面兩種可能性,每一個列表列出了 4 個方法調用的響應時間

A = {1, 1, 1, 1}
  B = {3.7, .1, .1, .1}

在 A 中響應時間是一致的,因此不管咱們省掉了哪兩個調用,最後響應時間都能縮短到 2 秒。但在 B 中,到底省掉哪兩個方法調用對響應時間的影響是有很大差異的。若是咱們去掉頭兩個調用,響應時間縮短爲 0.2 秒,提高了 95%。但若是咱們去掉的是後兩個調用,響應時間變爲 3.8 秒,僅僅提高了 5%。

偏斜度表達在一組值中的非一致性程度。正是由於偏斜度的存在,因此你無法準確的回答我在本節開頭的問題。 讓咱們再回頭看看這個例子:

例 9
在不知道偏斜度的前提下,你只能回答響應時間可能減小的範圍是在 0 到 1748.229 秒之間,這也是你能提供的最好的回答了。

儘管如此,假設你有一些額外的信息,如「表4」所示,你就能對最好和最壞的狀況進行估算。進一步說,假如你有了「表4」中信息就會很聰明的去特別優化響應時間在 0.01 秒到 0.1秒 之間的那 47,444 個調用。

10. 最小化風險

前面的章節我提到過,當修復一個任務性能問題時可能破壞另外一個任務的性能,讓我想起了一件曾經在丹麥發生的事。這個故事很短:

場景
在丹麥的巴勒魯普自治市(Måløv)的一張橡木餐桌前,大約 10 我的圍坐一塊兒,在用筆記本工做和相互交流。

Cary: 夥計們我快熱死了,大家不介意我打開窗戶放點冷空氣進來吧?
Carel-jan: 爲何你不把你的厚毛衣脫了呢?

完。

在這裏,有個樂觀的人都知道的通常性原則在發揮效力:__當你們都很溫馨除了你之外,那麼你首先應該確保影響本身的東西是否正常,不然你可能去搞亂一些全局的東西致使每個人都受影響。__

正是這個原則,當由於幾個寫的很爛的 Java 應用程序有人建議我去調整 Oracle 的網絡包大小時讓我感到很懼怕。這些很爛的程序產生了不少沒必要要的數據庫調用,天然也產生了不少沒必要要的網絡等待。當其餘一切正常除了這幾個爛程序,那麼最安全的作法是將調整的範圍本地化,只須要去調整這幾個爛程序就行了。

11. 效率

即使依賴此係統進行工做的全部人都很痛苦,你依然須要首先專一於業務上最優先須要修正的程序部分。讓程序工做的儘量的高效是一個很好的切入點。在不增長容量,不犧牲必須的業務功能的前提下,效率是可以節省下來的任務總執行時間的倒數。

換句話說,效率就是從反面對浪費進行的度量。下面是一些常常發生在數據庫應用中浪費的例子。

  • 一箇中間層程序爲插入數據庫的每條記錄建立了一條獨立的 SQL 語句。它執行了 10,000 次數據庫預編譯語句調用,致使了 10,000 次網絡 I/O 調用。其實它能夠只使用一條預編譯語句,從而節省 9,999 次網絡 I/O 調用。

  • 一個 SQL 語句訪問數據庫緩衝區緩存 7,428,322 次得到了 698 行的結果集。使用一個額外的過濾預測,只返回了終端用戶真正想要看見的 7 行結果,只需訪問緩衝區緩存 52 次。

確實若是一個系統存在一些全局性的問題(不良索引、錯誤參數、弱弱的硬件配置)致使了一大片任務執行的低效率,你應當修正它。但不要嘗試調優系統去知足低效的程序。有不少辦法來調優低效的程序自己。即便這個程序是商業的現成的軟件,那麼和你的軟件供應商一塊兒去優化程序自己比你去優化系統讓其儘量的高效從長期來講會更受益。

讓程序變的更高效會讓工做在這個系統上的每個人都受益巨大。很容易看到浪費的減小對任務響應時間的貢獻。但依然有不少人不明白爲何提高這部分程序的性能會致使一種反作用,讓看起來徹底不相關的另外一個程序性能變差。

其實這是系統負載在做祟。

12. 負載

負載(Load)是併發任務執行時引起的資源競爭。負載正是咱們爲何不能在性能測試中捕捉到全部性能問題的緣由,而這些問題之後會在生產環境發生。負載的一個測量指標是使用率,使用率反應了資源按時間分片的使用狀況。當某個資源使用率上升時,那麼請求該資源服務的用戶就不得不經歷更長的響應時間。任何一個在城市的高峯期開車的人都經歷過相似現象。當交通變的嚴重擁堵時,你不得不在收費站前等待更長的時間。

你的汽車在開闊的道路上能開上每小時 60 英里,但在擁堵的路上只能以每小時 30 英里的速度行駛,而軟件不會像汽車這樣真的變慢。軟件按照固定的一樣的速度執行,每一個時鐘週期老是執行一樣數量的指令,但響應時間會隨着系統資源變的繁忙而嚴重退化。

負載上升系統變慢的緣由有兩個:__隊列延遲__ 和 __相關性延遲__。下面我會進一步講述。

13. 隊列延遲

負載和響應時間之間在數學上的相關性你們都很熟悉了。一個叫作「M/M/m」的數學模型(譯註:「M/M/m」是一個關於隊列理論的數學模型,你無需詳細搞明白也能從感性認識並理解做者的分析。)將響應時間和負載關聯起來應用於一些特定的需求場景下。「M/M/m」模型的一個假設前提是你的系統模型擁有理論上的完美擴展性。這個假設很是相似於咱們在初級物理學課程中常常提到的光滑表面(無摩擦力)假設。

雖然「M/M/m」模型假設的條件有些不現實,如完美的可擴展性,但從中依然能夠學到不少。「圖4」使用「M/M/m」模型展現了負載和響應時間之間的關係。

從「圖4」,你從數學的角度看到了系統在不一樣負載條件下給你的感覺。低負載下的響應時間和無負載基本同樣。當負載上升時,你能感覺到響應時間有一個輕微、平緩的退化。這種平緩的變化不會形成什麼麻煩,但隨着負載繼續上升響應時間開始以一種急劇的方式退化,這可要形成大麻煩了。

響應時間在具有完美擴展性的「M/M/m」模型下由兩個部分組成:__服務時間__ 和 __隊列延遲__。

就是這樣一個等式:R = S + Q
服務時間(S)就是任務的執行時間。
隊列延遲(Q)就是任務在隊列中等待機會得到消費某個資源的時間。

因此當你在 Taco Tico(美國和墨西哥邊境的快餐連鎖)訂餐時,你的訂單響應時間(R)就包括了等待服務員來餐桌邊接收訂單的等待時間,這就是隊列延遲等待(Q),而服務時間(S)就是從訂單交到服務員時到食物送到你手上的等待時間。 一樣,任務的響應時間在有負載和無負載的系統之間是有差異的。

14. 拐點

說起性能,你想要達到兩個目標:

  • 你想要得到最快的響應時間:你不想任務的完成須要太長的時間。

  • 你想要得到最大的吞吐量:同一時間能支持更多人執行他們的任務。

不幸的是這兩個目標是相互矛盾的。__優化達到第一個目標須要你最小化系統的負載,而達到第二個目標則要最大化系統負載,兩者不可兼得。__ 在這二者之間的某個負載值就是系統的最優負載。

處於最優負載平衡點的資源使用率的值,我稱其爲「拐點」。系統中某種資源達到「拐點」後,那麼吞吐量被最大化了而對響應時間只有很小的負面影響。從數學上來說,「拐點」就是響應時間除以資源利用率所得結果最小的值。 「拐點」有個很好的屬性,就是位於從原點畫一條直線正好與響應時間曲線相切的位置。 在一個仔細繪製的「M/M/m」圖中,你能很容易的利用這個性質找到「拐點」,以下「圖5」所示。

關於「M/M/m」模型「拐點」的另外一個很好的屬性是你只須要知道一個參數就能夠計算出它。這個參數就是系統中並行的、相同的和獨立的服務通道數。服務通道是一種資源,它們共享一個隊列,其餘資源像收費站或者 SMP(Symmetric multiprocessing 對稱多處理)結構的計算機中的 CPU 都是相似的概念。

在「M/M/m」模型中,斜體小寫的 m 表示系統建模時服務通道數。對任意一個系統來講,計算「拐點」都是很困難的,好在我已經給你計算出來了。「表5」中列出了一些常見的服務通道數的「拐點」值。(此時你也許想知道在「M/M/m」隊列模型中另外兩個 M 表明什麼。它們與請求進入時刻和服務時間的隨機性假設有關。 更多請參考 Kendall's Notation 或進一步參考 _Optimizing Oracle Performance_)

爲什麼「拐點」如此重要?對於那些請求隨機到達的系統,若是資源負載持續超過「拐點」,那麼響應時間和吞吐會由於負載的輕微變化而嚴重波動。 因此,__對於請求隨機到達的系統而言,保持負載低於拐點是相當重要的。__

(譯註:從上面「表5」能夠看出,爲何經驗值將 4 核的虛擬化容器 CPU 負載紅色報警點在 60%,32或64 核物理機的 CPU 負載紅色報警點在 80%。)

15. 拐點的相關性

那麼「拐點」的概念是否是真的如此重要呢? 畢竟,我曾經告訴過你「M/M/m」模型創建在一個理想的烏托邦理念之上,那就是系統擁有完美的可擴展性。我知道你正在想什麼:你想的都是錯的。

「M/M/m」模型告訴咱們,即使你的系統擁有完美的可擴展性,你依然會遭遇巨大的性能問題只要系統的平均負載超過了圖表中給出的拐點。那麼現實中你的系統不可能比「M/M/m」假設的理論系統更完美。因此,你的系統的真實「拐點」會比我在「表5」中給出的更小。(我在這裏對拐點使用了複數形式,由於你能夠基於 CPU 來創建拐點模型,同時也能夠基於你的磁盤、網絡 I/O 等等。)

再次說明:

  • 你的系統中的每一項資源都有一個「拐點」。

  • 你的系統「拐點」都是小於或等於「表5」中給出的理論值,你的系統擴展的完美性越差,「拐點」越小。

  • 對於請求隨機到達的系統,若是資源負載持續超過「拐點」,你將遭遇性能問題。

因此,保持負載低於拐點是相當重要的。

(譯註:因此性能測試乾的就是找出真實系統的負載拐點,並和理論值比較就能夠看出系統的橫向擴展性是否有瓶頸點。)

16. 容量規劃

理解了「拐點」能夠減小容量規劃的複雜性,能夠這樣來規劃:

  • 某項資源的容量就是在高峯期能輕鬆的運行你的任務而資源使用率不會超過「拐點」。

  • 保持資源利用率低於「拐點」,那麼系統表現就基本不會給你帶來大的驚訝。

  • 可是,若是系統中任何一項資源超出了它們的「拐點」,你就會遭遇性能問題,不管你是否意識到。

  • 若是你遭遇性能問題,不要糾結於數學模型上,要修正這些問題要麼從新安排下負載分配,要麼減小負載,要麼增長容量。

這就是將性能管理過程和容量規劃結合起來的辦法。

17. 隨機到達

你可能已經注意到了,我在前文常常說起「隨機到達」這個說法,爲何它如此重要?如今一些系統擁有的特徵你可能不會具有,如:徹底肯定的做業調度。另一些系統被配置爲接受任務的方式像是機器人模式,如每秒接受一個任務,十分固定,固然如今這些系統不多見了。我這裏說的一秒一個任務,並非說平均一秒一個任務,例如第一秒 2 個任務,而下一秒 0 個任務。我指的是均勻的一秒來一個任務,相似汽車工廠組裝線上機器人的工做模式。

若是任務到達系統是徹底肯定的,就是說你徹底能預知下一個請求何時到達,那麼讓資源的使用率超過「拐點」必然不會引起性能問題。對於一個任務到達很肯定的系統,那麼你的目標應該是將資源利用到 100%,而不是讓它們排隊等待。

「拐點」對於隨機到達的系統如此重要的緣由是,隨機任務請求每每會彙集並引起短暫的資源使用脈衝式上升。這些脈衝式上升須要足夠的剩餘容量來消化它們,因此當脈衝發生時可能就會引起隊列延遲並致使響應時間的明顯起伏。

短暫的脈衝並致使資源使用率超過「拐點」也還好,只要不要持續達到數秒時間。這個數秒到底應該是多少秒呢? 我相信(固然我沒試過去證實)這個時間最好不要超過 8 秒。(來自著名的互聯網 8 秒原則) 若是你沒法知足在特定百分比下響應時間和吞吐量對用戶的承諾,那麼很顯然系統脈衝上升持續時間太長了。

18. 相關性延遲

你的系統確定不具有理論上的完美擴展性。儘管我從沒分析過你的系統,但我敢打賭不管你的系統不管是什麼樣的也不具有「M/M/m」理論模型假設的完美擴展性,而相關性延遲正是你的建模不可能完美的緣由。執行任務時花在對共享資源訪問的協商和通訊的時間就是相關性延遲。和響應時間、服務時間、隊列延遲同樣,相關性延遲也能夠在任務的執行中被測量,例如每點擊秒數。

這裏我並不想描述預測相關性延遲的數學模型,但若是你分析過你的任務執行狀況,你能夠了解何時相關性延遲會發生。在 Oracle 中,一些同步的事件正是相關性延遲的例子:

  • 入隊列(enqueue)

  • 緩衝忙等待(buffer busy waits)

  • 閂鎖釋放(latch free)

你不能使用「M/M/m」來對相關性延遲進行建模,由於「M/M/m」模型假設了你的 m 個服務通道是徹底並行的、等同的和獨立的。這個模型假設在一個先進先出(FIFO)隊列中,只要你等待的時間足夠長,在你以前的請求已出隊列並獲得服務,那麼最終你也會獲得服務,可是相關性延遲不是這樣工做的。

例 10
假設在一個 HTML 數據表單上,有個按鈕是「更新」,點擊它會執行一條 SQL 更新語句。另一個按鈕是「保存」,點擊它執行事務提交將剛纔的更新保存下來。若是一個應用是這樣作的,我能夠保證它的性能是很是糟糕的。這是由於這樣一種設計,讓下面的場景成爲可能的,實際上這也是必然可能的。一個用戶先點擊了「更新」,發現到了午飯時間,而後就去吃飯了,過了兩小時下午回來再點擊「保存」。

對於想要更新同一行的其餘任務來講,這是一個災難。其餘任務不得不等待獲取行鎖,更糟的狀況下甚至是頁鎖,直到原來鎖定的用戶想起繼續點擊「保存」。或者 DBA 來殺掉原來鎖定用戶的會話,這樣的話固然又會給原用戶形成錯覺,他覺得他更新了一行實際卻沒有,這可糟透了。

在這個例子中,無論系統繁忙與否,一個任務就在那無所事事的等待鎖的釋放。它依賴了系統資源利用率以外的一些隨機性因素。這就是爲何你不能使用「M/M/m」模型來對其進行建模。這也是爲何在一個單元測試環境下的性能測試結果不足以用來決策是否應該在生產環境添加一些新的代碼。

19. 性能測試

咱們談到的隊列延遲、相關性延遲引起了一個很困難的問題。你如何對一個新的應用進行足夠的測試,讓你信心滿滿的認爲它不爲由於性能問題而對你的生產程序形成破壞。你能夠去建模,也能夠去測試。可是,在你真正遭遇這些問題以前,爲全部你能夠預見的生產問題去創建模型和測試是極其困難的。

所以,一些人看到了這樣窘境,所以申辯說那麼就乾脆別測試了。千萬別被這樣的心態所困擾。下面的觀點是很肯定的:

  • 在程序進入生產環境以前,若是你嘗試去發現一些問題你確定會比那些徹底不去作的找到更多的問題。

  • 在預發佈的測試中,你不可能發現全部的問題,因此你須要一些可靠並有效的方法來解決這些在預發佈測試中漏掉的問題。

在徹底不測試和完整的生產環境模擬測試之間,存在一個適度測試量的平衡點。 固然對於一家飛機制造商來講,適度測試量確定多於一家銷售棒球帽的公司。但千萬別徹底跳過性能測試。至少,當你在生產環境遭遇不可避免的性能問題時,一份性能測試計劃將使你成爲一名更稱職的診斷專家(更清晰的思考者)。

20. 測量

人們能感知到的就是吞吐量和響應時間。吞吐量很容易測量,相對來講測量響應時間要稍微困難些。(還記得吧,吞吐量和響應時間可不是簡單的倒數關係)用個秒錶來計時終端用戶的行爲並不困難,但你不會從中獲得你真正想要的關於爲何響應時間如此之大的細節。

不幸的是,人們老是測量他們容易測量的,而不是他們應當測量的。 當咱們須要測量的東西不容易測量時,咱們就把注意力轉移到那些容易獲得測量數據上了,這是個錯誤。那些並非你真正須要的測量,但看起來彷佛和你真正須要的有些相關又容易去執行的測量,咱們稱之爲「替代指標」。 一些「替代指標」例子包括像子程序調用計數和子程序執行耗時的採樣數據。對於「替代指標」,很遺憾在個人母語中沒有更好的語句來表達個人想法,但有一個你們都熟悉的現表明達方式:__替代指標真是噁心。(Surrogate measures suck.)__

不幸的是,「噁心」在這裏並不表示它沒用。要是替代指標真的沒用就行了,那就沒人會使用它們了。問題就在於替代指標有時是有用的,這讓使用替代指標的人相信它們老是有用的,但實際並非這樣。

替代指標有兩個嚴重的問題:

  • 它們可能在系統不正常時告訴你係統一切正常,這在統計學上叫作第一型錯誤,假陽性。

  • 它們也可能在系統正常時告訴你係統出問題了,這在統計學上叫作第二型錯誤,假陰性。

這兩類錯誤浪費了人們許多的時間。

當你去評測一個真實系統方方面面,你可否取得成功在於你能從那個系統中得到多少有效的測量數據。我曾有幸在 Oracle 的市場部門工做過,那時許多軟件供應商圍繞着咱們積極的參與,這才使得正確的測量系統成爲可能。讓軟件開發者使用 Oracle 提供的工具是另一回事了,至少咱們的產品中具有這樣的能力(記錄有效的測量數據)。

21. 性能是一項功能特性

性能是軟件程序的一項功能特性,就像你在 Bug 跟蹤系統中很方便的將「Case 1234」這樣一個字符串自動連接到編號 1234 的 Bug 案例上。__性能像全部其餘軟件功能同樣,不是湊巧獲得的,你須要去設計和構建它。要想得到好的性能,你不得不去仔細的思考、研究、學習,寫出額外的代碼來測試和支持它。__

儘管如此,像全部其餘功能特性同樣,在項目初期你還在調研、設計和編寫代碼時你不可能知道性能到底會怎樣。對大多數應用(多是絕大多數,這個說法可能有爭議)而言性能都是未知的,直到它們投入實際使用階段。那麼留給你的問題就是:__由於在上線前你不可能知道你的應用性能表現到底怎樣,所以你須要在編寫應用時考慮怎樣很容易的在生產環境修復性能問題。__

正如大衛·加文(David Garvin)告訴咱們的,容易測量的東西也更容易管理(來自《創建一個學習型組織》1993年發表於《哈佛商業評論》) 那麼要寫一個在生產環境容易修復問題的應用程序,首先要作的就是要容易在生產環境進行測量。大多數時候,當我提到生產環境的性能測量時人們就會陷入一種焦慮狀態,他們很擔憂性能測量帶來的入侵效應。他們馬上對採集哪些數據作出了妥協,只留下那些「替代指標」(更容易採集的)在數據採集表上,擁有額外數據採集代碼的軟件會變的比沒有這些代碼的更慢麼?

我喜歡湯姆·凱特(Tom Kyte)之前對這個問題的回答。他估計額外的性能測量代碼給 Oracle 帶來不超過 10% 性能損失。他接着向那些氣惱的提問者做出解釋,正是由於從這些性能測量代碼獲取的數據讓 Oracle 公司進一步將產品性能改進提高了不止 10%,這超出了性能測量代碼自己引起的開銷。

我認爲不少軟件供應商他們一般花費了太多時間來優化他們的性能測量代碼路徑使其更高效,而不是首先搞清楚怎麼讓這些代碼有效果。 高德鈉(Donald Knuth)曾在 1974 說過的一句話印證了這個觀點:

過早優化是一切罪惡的根源。

軟件設計者將性能測量代碼整合進他們的產品中更有可能建立一個高性能的應用,更重要的是這個應用會不斷變的更快。

尾聲:關於「拐點」的公開辯論

在本文的 14 到 16 節,我描述了「拐點」的性能曲線、它們的相關性和應用。可是,在 20 年前有一場關因而否值得定義一個「拐點」概念的公開辯論。

歷史上的一個重要的觀點認爲我所描述的「拐點」並非真正有意義的。在 1988 年,斯蒂芬·薩姆森(Stephen Samson)爭論說至少在「M/M/1」的排隊系統的性能曲線中並不存在「拐點」。 他寫道:「選擇一個具有指導意義的數字並不容易,經驗法則仍是最適用的,在大多數狀況下都不存在拐點,不管你多麼但願找到一個。」

1999 年,溫水煮青蛙的故事啓發了我。這個故事是這樣的說的,當你把一隻青蛙扔進煮沸的開水中,它會馬上跳出來。但假如你先把它放在冷水中並慢慢的加熱水溫,青蛙會安靜的呆在水裏直到被煮熟了。對於資源使用率,它就像是沸水,有一個清晰的「死亡區間」。在這個區間值內,對於隨機到達的請求你的系統將不堪重負。那麼「死亡區間」的邊界在哪裏?若是你嘗試用程序來自動管理資源使用率,你就必須知道這個值。

最近,個人朋友尼爾·岡瑟(Neil Gunther)跟我有一場私下的辯論。首先,他認爲「死亡區間」這個術語使用在這裏是徹底錯誤的,由於在函數連續性的前提下使用「死亡區間」是錯誤的。 其次,他認爲對於「M/M/1」系統的「拐點」在 0.5 是過於浪費了,你應當更多的利用好系統資源,它應高於 0.5 的資源利用率。最後,他認爲你對使用率的明肯定義取決於實際的平均響應時間相對你能忍受的平均響應時間實際超出了多少。所以,岡瑟認爲任何有用的使用率閾值的定義都來源於詢問人們自身的偏好,而非來自於數學。(圖A)

從「圖B」中,咱們能夠看出這個說法的問題所在。 假設,你對平均響應時間的忍耐限度是 T,那麼對應的最大資源利用率是 ρT。你會看到在 ρT 附近資源利用率一個微小的變化都會致使響應時間巨大的波動。

我相信如我在第 4 節所寫的,__客戶感覺到的是差別變化,而非平均。__ 或許他們會說咱們可以接受平均響應時間達到 T,但我不相信人們能忍受由於系統平均負載發生了 1% 的變化致使平均響應時間達到 1 分鐘,換句話說就是平均響應時間翻了 10 倍。我確實瞭解我在 14 節列出的「拐點」列表比不少人直覺上感覺到地安全值更低一些,特別是對「低階」的系統如「M/M/1」而言。 儘管如此,但我相信避免由於資源使用率的微小變化引起響應時間的過大波動,這是極其重要的。

話雖如此,我也不知道該如何確切的定義「過大」這個詞。像響應時間波動的忍耐度,不一樣的人有不一樣的底線。或許有一個起伏忍耐的因子適用於全部人。例如,Apdex Standard Application Performance Index 假設了響應時間 FT 的 4 倍就會讓人們的態度從「滿意」變爲「煎熬」。

正如我在 16 節中描述的,「拐點」不管你怎麼去定義或稱呼它,對於容量規劃過程來講都是一個十分重要的參數。而且我相信它對平常的系統負載管理也是一個重要參數,我會繼續保持研究。

關於做者

Cary Millsap 是一家致力於軟件性能優化公司 Method R 的創始人和 CEO,是一位在 Oracle 全球社區著名的演講者、教育者、顧問和做者。曾和 Jeff Holt 合著 Optimizing Oracle Performance 一書,更多詳細信息參見做者 LinkedIn 的介紹和我的博客

參考

  1. CMG (Computer Measurement Group, a network of professionals who study these problems very, very seriously); http://www.cmg.org.

  2. Eight-second rule; http://en.wikipedia.org/wiki/...

  3. Garvin, D. 1993. Building a learning organization. Harvard Business Review (July).

  4. General Electric Company. What is Six Sigma? The roadmap to customer impact. http://www.ge.com/sixsigma/Si...

  5. Gunther, N. 1993. Universal Law of Computational Scalability; http://en.wikipedia.org/wiki/...

  6. Knuth, D. 1974. Structured programming with Go To statements. ACM Computing Surveys 6(4): 268.

  7. Kyte, T. 2009. A couple of links and an advert...; http://tkyte.blogspot.com/200...

  8. Millsap, C. 2009. My whole system is slow. Now what? http://carymillsap.blogspot.c...

  9. Millsap, C. 2009. On the importance of diagnosing before resolving. http://carymillsap.blogspot.c...

  10. Millsap, C. 2009. Performance optimization with Global Entry. Or not? http://carymillsap.blogspot.c...

  11. Millsap, C., Holt, J. 2003. Optimizing Oracle Performance. Sebastopol, CA: O'Reilly.

  12. Oak Table Network; http://www.oaktable.net.


寫點文字,畫點畫兒。
微信公衆號「瞬息之間」,碰見了不妨就關注看看。

相關文章
相關標籤/搜索