第三章 服務器性能剖析 高性能MySql閱讀總結

????????在我們的技術咨詢生涯中,最常碰到的三個性能相關的服務請求是:如何確認服務器是否達到了性能最佳的狀態、找出某條語句為什么執行不夠快,以及診斷被用戶描述成“停頓” 、“堆積” 或者”卡死” 的某些間歇性疑難故障。本章將主要針對這三個問題做出解答。我們將提供一些工具和技巧來優化整機的性能、優化單條語句的執行速度, 以及診斷或者解決那些很難觀察到的問題(這些問題用戶往往很難知道其根源,有時候甚至都很難察覺到它的存在)。

????????這看起來是個艱巨的任務,但是事實證明,有一個簡單的方法能夠從噪聲中發現苗頭。這個方法就是專注于測量服務器的時間花費在哪里,使用的技術則是性能剖析(profiling)。在本章,我們將展示如何測最系統并生成剖析報告, 以及如何分析系統的整個堆棧(stack), 包括從應用程序到數據庫服務器到單個查詢。

3.1性能優化簡介

????????問10個人關于性能的問題,可能會得到10個不同的回答,比如“每秒查詢次數” 、"cpu利用率” 、“ 可擴展性” 之類。這其實也沒有問題,每個人在不同場景下對性能有不同的理解,但本章將給性能一個正式的定義。我們將性能定義為完成某件任務所需要的時間度量,換句話說,性能即響應時間,這是一個非常重要的原則。我們通過任務和時間而不是資源來測最性能。數據庫服務器的目的是執行SQL語句,所以它關注的任務是查詢或者語句,如SELECT、UPDATE、DELETE等。數據庫服務器的性能用查詢的響應時間來度量, 單位是每個查詢花費的時間。

????????還有另外一個問題:什么是優化?我們暫時不討論這個問題, 而是假設性能優化就是在一定的工作負載下盡可能地 降低響應時間。

????????很多人對此很迷茫。 假如你認為性能優化是降低CPU利用率, 那么可以減少對資源的使用。 但這是一個陷阱, 資源是用來消耗并用來工作的, 所以有時候消耗更多的資源能夠加快查詢速度。 很多時候將使用老版本InnoDB引擎的MySQL升級到新版本后,CPU利用率會上升得很厲害, 這并不代表性能出現了問題, 反而說明新版本的InnoDB對資 源的利用率上升了。 查詢的響應時間則更能體現升級后的性能是不是變得更好。 版本升級有時候會帶來一些bug, 比如不能利用某些索引從而導致CPU利用率上升。 CPU利用率只是一種現象, 而不是很好的可度量的目標。

????????同樣, 如果把性能優化僅僅看成是提升每秒查詢量, 這其實只是吞吐量優化。 吞吐量的提升可以看作性能優化的副產品。對查詢的優化可以讓服務器每秒執行更多的查詢, 因為每條查詢執行的時間更短了(吞吐量的定義是單位時間內的查詢數最, 這正好是我們對性能的定義的倒數)。

????????所以如果目標是降低響應時間, 那么就需要理解為什么服務器執行查詢需要這么多時間, 然后去減少或者消除那些對獲得查詢結果來說不必要的工作。 也就是說, 先要搞清楚時間花在哪里。 這就引申出優化的第二個原則:無法測量就無法有效地優化。 所以第一步應該測量時間花在什么地方。

? ??????我們觀察到,很多人在優化時,都將精力放在修改一些東西上,卻很少去進行精確的測量。我們的做法完全相反, 將花費非常多, 甚至90%的時間來測量響應時間花在哪里。 如果 通過測量沒有找到答案, 那要么是測最的方式錯了, 要么是測量得不夠完整。 如果測量 了系統中完整而且正確的數據, 性能問題一般都能暴露出來, 對癥下藥的解決方案也就比較明了。 測量是一項很有挑戰性的工作, 并且分析結果也同樣有挑戰性, 測出時間花 在哪里,和知道為什么花在那里, 是兩碼事。

????????前面提到需要合適的測量范圍, 這是什么意思呢?合適的測量范圍是說只測量需要優化 的活動。 有兩種比較常見的情況會導致不合適的測量:

? 在錯誤的時間啟動和停止測量。

? 測量的是聚合后的信息,而不是目標活動本身。

????????例如, 一個常見的錯誤是先查看慢查詢, 然后又去排查整個服務器的情況來判斷問題在哪里。 如果確認有慢查詢, 那么就應該測量慢查詢,而不是測量整個服務器。 測量的應該是從慢查詢的開始到結束的時間,而不是查詢之前或查詢之后的時間。

????????完成一項任務所需要的時間可以分成兩部分:執行時間和等待時間。 如果要優化任務的執行時間, 最好的辦法是通過測量定位不同的子任務花費的時間, 然后優化去掉一些子任務、 降低子任務的執行頻率或者提升子任務的效率。 而優化任務的等待時間則相對要復雜一些, 因為等待有可能是由其他系統間接影響導致, 任務之間也可能由于爭用磁盤或者CPU資源而相互影響。 根據時間是花在執行還是等待上的不同,診斷也需要不同的 工具和技術。

????????剛才說到需要定位和優化子任務, 但只是一筆帶過。 一些運行不頻繁或者很短的子任務對整體響應時間的影響很小, 通常可以忽略不計。 那么如何確認哪些子任務是優化的目標呢?這個時候性能剖析就可以派上用場了。

????????如何判斷測量是正確的?如果測量是如此重要,那么測量錯了會有什么后果?實際上,測量經常都是錯誤的。 對數量的測量并不等于數量本身。 測量的錯誤可能很小,跟實際情況區別不大, 但 錯的終歸是錯的。 所以這個問題其實應該是:"測量到底有多么不準確?” 這個問 題在其他一些書中有詳細的討論, 但不是本書的主題。 但是要意識到使用的是測量 數據, 而不是其所代表的實際數據。 通常來說, 測量的結果也可能有多種模糊的表現, 這可能導致推斷出錯誤的結論。

3.1.1通過性能剖析進行優化

????????一且掌握井實踐面向響應時間的優化方法, 就會發現需要不斷地對系統進行性能剖析(profiling)。

????????性能剖析是測量和分析時間花費在哪里的主要方法 性能剖析一般有兩個步驟:測量任務所花費的時間,然后對結果進行統計和排序, 將重要的任務排到前面

????????性能剖析工具的工作方式基本相同。 在任務開始時啟動計時器, 在任務結束時停止計時 ?器, 然后用結束時間減去啟動時間得到響應時間。 也有些工具會記錄任務的父任務。 這些結果數據可以用來繪制調用關系圖, 但對于我們的目標來說更重要的是, 可以將相似的任務分組并進行匯總。 對相似的任務分組并進行匯總可以幫助對那些分到一組的任務做更復雜的統計分析, 但至少需要知道每一組有多少任務, 并計算出總的響應時間。 通過性能剖析報告 (profile report) 可以獲得需要的結果。 性能剖析報告會列出所有任務列表。 每行記錄一個任務, 包括任務名、 任務的執行時間、 任務的消耗時間、 任務的平均執行時間, 以及該任務執行時間占全部時間的百分比。 性能剖析報告會按照任務的消耗 時間進行降序排序。

????????為了更好地說明, 這里舉一個對整個數據庫服務器工作負載的性能剖析的例子, 主要輸出的是各種類型的查詢和執行查詢的時間。 這是從整體的角度來分析響應時間, 后面會演示其他角度的分析結果。 下面的輸出是用 Percona Toolkit 中的pt-query-digest (實際上就是著名的 Maatkit 工具中的 mk-query-digest) 分析得到的結果。 為了顯示方便, 對結果做了一些微調, 并且只截取了前面幾行結果:

????????上面只是性能剖析結果的前幾行, 根據總響應時間進行排名, 只包括剖析所需要的最小列組合。 每一行都包括了查詢的響應時間和占總時間的百分比、 查詢的執行次數、 單次執行的平均響應時間, 以及該查詢的摘要。 通過這個性能剖析可以很清楚地看到每個查詢相互之間的成本比較, 以及每個查詢占總成本的比較。 在這個例子中, 任務指的就是查詢, 實際上在分析 MySQL 的時候經常都指的是查詢。

????????我們將實際地討論兩種類型的性能剖析:基于執行時間的分析和基于等待的分析。基于執行時間的分析研究的是什么任務的執行時間最長, 而基于等待的分析則是判斷任務在什么地方被阻塞的時間最長

????????如果任務執行時間長是因為消耗了太多的資源且大部分時間花費在執行上, 等待的時間 不多, 這種情況下基于等待的分析作用就不大。 反之亦然, 如果任務一直在等待, 沒有消耗什么資源, 去分析執行時間就不會有什么結果。 如果不能確認問題是出在執行還是等待上, 那么兩種方式都需要試試。 后面會給出詳細的例子。

????????事實上, 當基于執行時間的分析發現一個任務需要花費太多時間的時候, 應該深入去分析一下, 可能會發現某些 “執行時間” 實際上是在等待。 例如, 上面簡單的性能剖析的 輸出顯示表 InvitesNew上的 SELECT 查詢花費了大量時間, 如果深入研究, 則可能發現時間都花費在等待I/O完成上。

????????在對系統進行性能剖析前,必須先要能夠進行測量,這需要系統可測量化的支持。 可測量的系統一般會有多個測最點可以捕獲井收集數據,但實際系統很少可以做到可測量化。大部分系統都沒有多少可測址點,即使有也只提供一些活動的計數,而沒有活動花費的時間統計。 MySQL就是一個典型的例子,直到版本5.5才第一次提供了Performance Schema, 其中有一些基于時間的測量點 , 而版本5.1及之前的版本沒有任何基于時間的測量點。能夠從MySQL收集到的服務器操作的數據大多是show status計數器的形式,這些計數器統計的是某種活動發生的次數。 這也是我們最終決定創建Percona Server的主要原因,PerconaServer從版本5.0開始提供很多更詳細的查詢級別的測址點。

????????雖然理想的性能優化技術依賴于更多的測量點,但幸運的是,即使系統沒有提供測量點,也還有其他辦法可以展開優化工作。 因為還可以從外部去測量系統,如果測量失敗,也可以根據 對系統的了解做出一些靠譜的猜測。 但這么做的時候一定要記住,不管是外部測量還是猜測,數據都不是百分之百準確的,這是系統不透明所帶來的風險。

? ??????舉個例子,在Percona Server 5.0中,慢查詢日志揭露了一些性能低下的原因,如磁盤I/O等待或者行級鎖等待。 如果日志中顯示一條查詢花費10秒,其中 9.6 秒在等待磁盤I/O,那么追究其他4%的時間花費在哪里就沒有意義,磁盤1/0才是最重要的原因。

3.1.2理解性能剖析

????????MySQL的性能剖析(profile)將最重要的任務展示在前面,但有時候沒顯示出來的信息也很重要。 可以參考一下前面提到過的性能剖析的例子。 不幸的是,盡管性能剖析輸出 了排名 、 總計和平均值,但還是有很多需要的信息是缺失的,如下所示。

值得優化的查詢(worthwhile query)

????????性能剖析不會自動給出哪些查詢值得花時間去優化。 這把我們帶回到優化的本意,如果你讀過Cary Millsap的書, 對此就會有更多的理解。 這里我們要再次強調兩點: 第一 一些只占總響應時間比重很小的查詢是不值得優化的。 根據阿姆達爾定律(Amdahl's Law), 對一個占總響應時間不超過 5% 的查詢進行優化,無論如何努力,收益也不會超過 5%。 第二,如果花費了1 000 美元去優化一個任務,但業務的收入沒有任何增加,那么可以說反而導致業務被逆優化了1 000 美元。 如果優化的成本大于收益,就應當停止優化。

異常情況

????????某些任務即使沒有出現在性能剖析輸出的前面也需要優化。 比如某些任務執行次數很少, 但每次執行都非常慢,嚴重影響用戶體驗。 因為其執行頻率低,所以總的響應時間占比井不突出。

未知的未知

????????一款好的性能剖析工具會顯示可能的 “丟失的時間”。丟失的時間指的是任務的總時間和實際測量到的時間之間的差。 例如,如果處理器的CPU時間是10秒, 而剖析 到的任務總時間是9.7秒, 那么就有300毫秒的丟失時間。 這可能是有些任務沒有測量到, 也可能是由于測量的誤差和精度問題的緣故。 如果工具發現了這類問題, 則要引起重視,因為有可能錯過了某些重要的事情。即使性能剖析沒有發現丟失時間, 也需要注意考慮這類問題存在的可能性, 這樣才不會錯過重要的信息。 我們的例子中沒有顯示丟失的時間, 這是我們所使用工具的一個局限性。

被掩藏的細節

????????性能剖析無法顯示所有響應時間的分布。 只相信平均值是非常危險的, 它會隱藏很多信息, 而且無法表達全部情況。Peter經常舉例說醫院所有病人的平均體溫沒有任何價值。假如在前面的性能剖析的例子的第一項中,如果有兩次查詢的響應時間是 1秒, 而另外12 771次查詢的響應時間是幾十微秒, 結果會怎樣?只從平均值里是 無法發現兩次1秒的查詢的。 要做出最好的決策, 需要為性能剖析里輸出的這一行中包含的12773次查詢提供更多的信息,尤其是更多響應時間的信息,比如直方圖、百分比、標準差、偏差指數等。

????????好的工具可以自動地獲得這些信息。 實際上,pt-query-digest就在剖析的結果里包含了很多這類細節信息, 并且輸出在剖析報告中。 對此我們做了簡化, 可以將精力集中在重要而基礎的例子上:通過排序將最昂貴的任務排在前面。 本章后面會展示更多豐富而有用的性能剖析的例子。

????????在前面的性能剖析的例子中, 還有一個重要的缺失,就是無法在更高層次的堆棧中進行交互式的分析。 當我們僅僅著眼于服務器中的單個查詢時, 無法將相關查詢聯系起來,也無法理解這些查詢是否是同一個用戶交互的一部分。 性能剖析只能管中窺豹, 而無法將剖析從任務擴展至事務或者頁面查看(page view)的級別。 也有一些辦法可以解決這 個問題,比如給查詢加上特殊的注釋作為標簽, 可以標明其來源井據此做聚合, 也可以在應用層面增加更多的測量點, 這是下一節的主題。

3.2對應用程序進行性能剖析

????????對任何需要消耗時間的任務都可以做性能剖析, 當然也包括應用程序。 實際上, 剖析應用程序一般比剖析數據庫服務器容易, 而且回報更多。 雖然前面的演示例子都是針對MySQL服務器的剖析,但對系統進行性能剖析還是建議自上而下地進行,這樣可以追蹤自用戶發起到服務器響應的整個流程。 雖然性能問題大多數情況下都和數據庫有關,但應用導致的性能問題也不少。 性能瓶頸可能有很多影響因素:

? 外部資源, 比如調用了外部的Web服務或者搜索引擎。

? 應用需要處理大量的數據, 比如分析一個超大的XML文件。

? 在循環中執行昂貴的操作, 比如濫用正則表達式。

? 使用了低效的算法, 比如使用暴力搜索算法(naive search algorithm)來查找列表中的項。

????????幸運的是,確定MySQL的問題沒有這么復雜,只需要一款應用程序的剖析工具即可(作為回報, 一且擁有這樣的工具, 就可以從一開始就寫出高效的代碼)。

????????建議在所有的新項目中都考慮包含性能剖析的代碼。 往已有的項目中加入性能剖析代碼 也許很困難, 新項目就簡單一些。

3.3剖析MySQL查詢

????????對查詢進行性能剖析有兩種方式,可以剖析整個數據庫服務器,這樣可以分析出哪些查詢是主要的壓力來源(如果已經在最上面 的應用層做過剖析,則可能已經知道哪些查詢需要特別留意)。定位到具體需要優化的 查詢后,也可以鉆取下去對這些查詢進行單獨的剖析分析哪些子任務是響應時間的主 要消耗者。

3.3.1剖析服務器負載

????????服務器端的剖析很有價值,因為在服務器端可以有效地審計效率低下的查詢。如果只是需要剖析并找出代價高的查詢,就不需要如此復雜。有一個工具很早之前就能幫到我們了,這就是慢查詢日志。

? ??????在MySQL的當前版本中,慢查詢日志是開銷最低、精度最高的測量查詢時間的工具。慢查詢日志帶來的開銷可以忽略不計(實際上在CPU密集型場景的影響還稍微大一些)。更需要擔心的是日志可能消耗大量的磁盤空間。如果長期開啟慢查詢日志,注意要部署日志輪轉(log rotation)工具。或者不要長期啟用慢查詢日志,只在需要收集負載樣本的期間開啟即可。

分析查詢日志

????????不要直接打開整個慢查詢日志進行分析,這樣做只會浪費時間和金錢。首先應該生成一個剖析報告,如果需要,則可以再查看日志中需要特別關注的部分。自頂向下是比較好 的方式,否則有可能像前面提到的,反而導致業務的逆優化。

3.4診斷間歇性問題

? ? ? ? 盡量不要使用試錯的方式來解決問題。這種方式有很大的風險,因為結果可能變得更壞。這也是種令人沮喪且低效的方式。如果一時無法定位問題,可能是測量的方式不正確, 或者測量的點選擇有誤,或者使用的工具不合適(也可能是缺少現成的工具,我們已經開發過工具來解決各個系統不透明導致的問題,包括從操作系統到MySQL都有)。

為了演示為什么要盡量避免試錯的診斷方式, 下面列舉了我們認為已經解決的一些間歇性數據庫性能問題的實際案例:

? 應用通過curl從一個運行得很慢的外部服務來獲取匯率報價的數據。

? memcached緩存中的一些重要條目過期,導致大最請求落到M ySQL以重新生成緩存條目。

? DNS查詢偶爾會有超時現象。

? 可能是由于互斥鎖爭用,或者內部刪除查詢緩存的算法效率太低的緣故,MySQL的查詢緩存有時候會導致服務有短暫的停頓。

? 當并發度超過某個閩值時,InnoDB的擴展性限制導致查詢計劃的優化需要很長的時間。

3.6總結

????????本章給出了一些基本的思路和技術, 有助千你成功地進行性能優化。 正確的思維方式是開啟系統的全部潛力和應用本書其他章節提供的知識的關鍵。 下面是我們試圖演示的一 些基本知識點:

? 我們認為定義性能最有效的方法是響應時間。

? 如果無法測量就無法有效地優化, 所以性能優化工作需要基于高質量、 全方位及完整的響應時間測量。

? 測量的最佳開始點是應用程序, 而不是數據庫。 即使問題出在底層的數據庫, 借助良好的測量也可以很容易地發現問題。

? 大多數系統無法完整地測量, 測量有時候也會有錯誤的結果。 但也可以想辦法繞過 一些限制, 并得到好的結果(但是要能意識到所使用的方法的缺陷和不確定性在哪里)。

? 完整的測量會產生大量需要分析的數據, 所以需要用到剖析器。 這是最佳的工具, 可以幫助將重要的問題冒泡到前面, 這樣就可以決定從哪里開始分析會比較好。

? 剖析報告是一種匯總信息, 掩蓋和丟棄了太多細節。 而且它不會告訴你缺少了什么, 所以完全依賴剖析報告也是不明智的。

? 有兩種消耗時間的操作:工作或者等待。 大多數剖析器只能測暈因為工作而消耗的時間, 所以等待分析有時候是很有用的補充, 尤其是當 CPU 利用率很低但工作卻一直無法完成的時候。

? 優化和提升是兩回事。 當繼續提升的成本超過收益的時候, 應當停止優化。

????????總體來說, 我們認為解決性能問題的方法, 首先是要澄清問題, 然后選擇合適的技術來 解答這些問題。 如果你想嘗試提升服務器的總體性能, 那么一個比較好的起點是將所有查詢記錄到日志中, 然后利用pt-query-digest工具生成系統級別的剖析報告。 如果是要追查某些性能低下的查詢, 記錄和剖析的方法也會有幫助。 可以把精力放在尋找那些消耗時間最多的、 導致了糟糕的用戶體驗的, 或者那些高度變化的, 抑或有奇怪的響應時 間直方圖的查詢。 當找到了這些 “壞 ” 查詢時, 要鉆取pt-query-digest報告中包含的該查詢的詳細信息, 或者使用 SHOW PROFILE 及其他諸如 EXPLAIN 這樣的工具。

????????如果找不到這些查詢性能低下的原因, 那么也可能是遇到了服務器級別的性能問題。 這 時, 可以較高精度測量和繪制服務器狀態計數器的細節信息。 如果通過這樣的分析重現了問題, 則應該通過同樣的數據制定一個可靠的觸發條件, 來收集更多的診斷數據。 多花費一點時間來確定可靠的觸發條件, 盡量避免漏檢或者誤報。 如果已經可以捕獲故障活動期間的數據,但還是無法找到其根本原因, 則要么嘗試捕獲更多的數據, 要么嘗試尋求幫助。

????????我們無法完整地測量工作系統,但說到底它們都是某種狀態機, 所以只要足夠細心, 邏輯清晰井且堅持下去, 通常來說都能得到想要的結果。 要注意的是不要把原因和結果搞混了, 而且在確認問題之前也不要隨便針對系統做變動。

????????理論上純粹的自頂向下的方法分析和詳盡的測量只是理想的情況, 而我們常常需要處理的是真實系統。 真實系統是復雜且無法充分測量的, 所以我們只能根據情況盡力而為。使用諸如pt-query-digest和MySQL企業監控器的查詢分析器這樣的工具并不完美, 通常都不會給出問題根源的直接證據。 但真的掌握了以后, 已經足以完成大部分的優化診斷工作了。


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容