系統性能優化二三事

       工做中當一個業務系統被開發出來以後,通過屢次迭代業務的發展處理邏輯會愈來愈複雜,同時訪問量以及處理的數據也會相應的增多,系統的響應時間就會開始得逐漸加長。終於有一天用戶忍受不了抱怨你的app或者頁面要等好幾十秒或者好幾分鐘才響應時你就迫切須要對你的系統進行一個性能的優化。git

      下面談一下我對性能優化方面的一些觀點和看法,包括分析系統爲何會慢,以及針對這些慢的情形如何選取更合理的解決方案。github

      系統爲何慢?redis

      提及系統慢的緣由能舉的例子太多了,好比某人寫的代碼寫得太爛了,這個sql查詢寫得太差了,循環調服務啊等等。那拋開我的編碼的能力問題有沒有一些是與系統自己的設計相關的呢。好比系統原先是一個單體應用,後面作了業務的垂直拆分拆出了N個子系統而且作了一個分佈式的部署變成了一個分佈式系統,那麼系統的響應時間會不會所以變長呢。答案几乎是確定的!爲何呢算法

             

      舉一個電商系統的例子,左邊是系統初期的樣子,基本上全部業務模塊都放在了一塊兒,右邊是後面作了業務的垂直拆分以後的樣子。咱們看到這些業務模塊間的調用是由進程/線程變成了一個遠程的RPC網絡調用,這裏就多出了網絡響應時間(RT)的延遲 ,試想一個電商的成單過程要調商品、結算、庫存、用戶、積分等N個系統。假設每一個RT時間都是2ms那麼成單就有2Nms的時間是在等待數據傳輸。這裏面還並無算上這些N子系統的自己的處理時間而且它們極可能也會調用其它外部的子系統。因此你知道像阿里這樣的公司在雙11的以前爲何作全鏈路的優化了吧,其中一個緣由是由於當一個單體應用被拆成一個分佈式系統後調用鏈路變長了,而且每一個鏈路至少多了一個RT的響應延遲。因此咱們知道有時候系統變慢並非某個開發的鍋,而是由於系統架構致使的。對於這種情形咱們就能夠優化RT延遲,解決辦法能夠是申請專線或者把應用部署在同一個物理機房裏面,反正原理就是不能讓咱們的數據走太長的路~。spring

      從上面這個例子咱們知道在作系統優化以前分析是第一步也是最重要的一步。說到分析有些問題能夠在咱們腦中進行思惟邏輯推導分析就能夠定位到問題的。但現實中絕大部分問題都是不行的!都是要用數聽說話的!因此監控是很重要的,沒有全鏈路的跟蹤,只是靠猜作優化只會事倍功半!全鏈路的跟蹤工具這裏推薦一下大衆點評cat https://github.com/dianping/cat 和 zipkin https://zipkin.io/sql

    

   下面列幾個常見的問題分析是如何作優化選型的和一些系統中你可能沒有注意到有性能問題的地方。數組

    大量讀多少許寫是主從仍是緩存?緩存

    說到這個問題我相信不少人的第一反應的都是說主從,確實在個人工做經歷中絕大部分人在這個問題第一反應也是主從,其中不乏包括一些在大廠待過而且工做經驗是我一倍多的老前輩。我本身以爲主從仍是緩存得看"量"有多大。這個"量"不是指有多少用戶在訪問,而是用戶訪問的這個數據到底有大。若是用戶訪問的這個數據很小,小到用緩存就能夠放得下,我以爲這時用緩存是更好的方案。爲何?由於主從之間的數據同步延遲是一個很頭疼的問題!特別是一主多從的狀況下。。。。而且弄從庫的成本會很高爲了解決主從不一樣步的問題還須要調研一個數據中間件,而且性能尚未緩存高,落地成本大,時間長!只有在用戶訪問的數據量太大,大到不能用緩存放得下我以爲這時候才考慮用從庫,而且要充分考慮數據不一樣步的影響!以前我看過58架構師沈劍的文章,他也提到58是用緩存來解決讀多寫少問題,讀緩存,寫主寫時更新緩存,從庫只做爲數據備份做爲容災。然而工做中我也確實遇到過一個庫的數據只有幾千條但弄出了一個從庫的經歷,汗。。。。tomcat

   

    依賴很是多的外部RPC服務時性能優化

    我曾經遇到服務器端返回一個頁面展現的數據要調用兩位數的外部RPC服務,而且這個頁面是用戶常常訪問到的並且頁面數據還真不能作緩存,數據必須得實時刷。這個頁面每次一有大促的時候必須得加機器而且加了仍是有點扛不住的樣子。後面優化這塊代碼的時候我發現有幾個RPC服務不是相互依賴它們是各自返回部分的數據。我就把這幾個RPC換成異步調用的形式,後面的性能就好不少了,雖然大促時仍是要加機器,但cpu和響應時間指標比優化以前降了許多。這裏用到的祕訣就是化同步爲異步,化串行爲並行。而且這一招在不少狀況都是頗有效果的!    

   

   如何配置線程池

   線程池也是一個性能優化重點,線程池配置得好的話就能很好利用現代計算機的多核處理器去提高系統的性能,可是配置得很差的話,就很差說了~。線程池參數很重要的一個參數就是設置多少個線程。通常來講線程的數量是與你處理的任務特性是相關的,好比你的任務是重計算輕IO的話,那麼你的線程數最好不要太多通常Ncpu + 1 或者Ncpu就夠了。由於太多的線程會致使CPU競爭劇烈不停的進行上下文切換,這反而是事倍功半。若是是IO密集型任務的話則能夠根據自已須要的配多一點線程好比2Ncpu。若是是混合型任務那最好把二者分開,獨立用線程池是處理。另外若是你是用JAVA的話最好是去了解一下Executor的框架,看一下JDK的源碼實現,由於JAVA有好幾種線程池:FixedThreadPool,SingleThreadExecutor,CachedThreadPool,瞭解一下它們的適用情景對你的選型是頗有好處的。同時你也要留意一下你部署機器的CPU核數,千萬不要在單核的機器上啓用多線程。另外要注意了單線程模型也並不必定表明性能底下哦,好比redis就是單線程模型。爲何高呢?其中一個緣由是它沒有CPU上下文切換啊。

   

    DB查詢性能優化

    這一塊我以爲要講的東西很是多,有時間的話打算專門寫一篇文章來介紹。這裏略過鳥。。。

   

    系統中陳舊的框架

   不知道你們的項目裏面是否是還有用c3p0鏈接池的,這個東西我在出來工做那會兒用過以後就再也沒有用過了。由於這個線程池性能確實不高,而且工做中也常常報問題。後面我用的是druid再後來是spring-boot的tomcat-jdbc,hikaricp。由於技術老是不停的在前進,新的設計老是不斷改良舊的設計。而且一些陳舊的框架後面就再沒有更新了,好比c3p0吧,它是用JAVA語言寫的它出來那會兒當時JDK用來同步操做還只能是synchronized,因此意味着當時語言特性影響限制它的性能上限。如今JDK和當時的相比可不同了增長了併發包,從虛擬機到咱們經常使用的數據結構和API都作了不少優化和改良。因此框架也是不斷更新的纔好。

   

   了解熟悉你經常使用數據結構、中間件和系統網絡知識。

   好比redis是單線程模型,單線程意味着redis的全部任務都只能按順序的一個個去處理,前面的沒處理完後面就只能安靜的等待着。前面的卡住後面就會有堆積,就會產生超時影響redis的性能,因此redis裏面不能有重計算的操做和存大key,若是要存大key最好應用服務器本身壓縮一下才存進去,一方面能夠減小性能影響也能夠節省帶寬~。網絡協議TCP和UDP這些就不用說了。HTTP和HTTPS二者的性能差別聽說是4倍左右,由於HTTPS每次發接收報文都要加解密,運算耗費CPU。因此一些數據不敏感的是否是考慮用HTTP會好一點~。JAVA經常使用的數據ArrayList,LinkedList,Set,HashMap,Array,TreeMap這些低層的源碼最好也看一下,瞭解一下適用場景,順便也學習一下別人的代碼。我舉個例子以前我作算法題的時候有道題用HashMap和Array都是能夠AC的,可是二者的出來的時間差了一個數量級,緣由是HashMap和Array的低層實現致使的。HashMap低層是一個數組鏈表,而且存儲的對象是Node結點,Hash衝突大的時候其時間複雜度就由O(1) 退化成O(n),而且還有一個鮮爲人知的擴容過程,Array數組就是肯定了每一次讀取操做都O(1)。因此執行n次操做後二者的耗費的時間就是O(n)平方與O(n)的差異了。。。。不過HashMap在JAVA8終於作了改良,當鏈表的長度大於8的時候就換成了紅黑樹的存儲這時複雜度降爲了O(log(n)),這些你看下源碼就一目瞭然。另外操做系統這塊也是要了解的好比epoll select poll的區別,線程用戶態內核態的切換等等。

   

   養成良好的我的編碼習慣和意識。

   好比不要寄望在一個SQL裏面作完全部的事情,由於一般是寫一個複雜的SQL一分鐘,可能優化你一年。調用N次RPC處理N個數據不如調一次RPC處理N個數據,由於能夠節省了(N-1)的RT時間,一樣對SQL查詢也適用。多瞭解一下一些新語言的特性和設計思想好比forkJoin,JAVA8的lambda表達式,completefuture異步處理。或者有時間精力學習一下算法知識也是能夠開闊一下你的思路。

 

  另外普及一下性能優化逃不開的一個原理就是木桶原理,就是系統的總體性能的好壞不是取決於你最好的那部分,而是最差的那部分·-·。這部分多是在你係統內部也多是外部。因此性能優化的路很長很長很長很長長長。。。

相關文章
相關標籤/搜索