Java性能調優實戰 --- 億級流量系列 ---
掘金 --- author : 樹下搜胡
正則表達式
本文主要說明經過服務部署看性能是否提高的實踐試驗方法算法
若有須要,能夠參考數據庫
若有幫助,不忘 點贊 ❥編程
微信公衆號已開啓,cto聯盟,沒關注的同窗們記得關注哦!設計模式
前幾天跟 某互聯網主題旅遊網架構師聊起性能調優的話題,那個時候正好遇到一些線上調優的一些困惑,但願一塊兒探討一下。緩存
他跟我說,他們公司的系統歷來沒有通過性能調優,功能測試完成後就上線了,線上也沒有出現過什麼性能問題呀,爲何要進行調優呢??性能優化
可謂底氣十足啊!!! 無知者無畏啊;就不怕爲公司埋雷,到時候追究他的責任?微信
當時我一臉懵逼,既然不作性能優化就直接上線,至關於給系統埋了一個定時炸彈,一旦到某個臨界點,說炸就炸,到時候迴天乏力;markdown
所以我就回答了他一句,「我去,若是大家公司作的是淘寶,京東,12306 這樣的網站,不作系統性能優化就上線,試試看會是什麼狀況」。網絡
今天,咱們就從這個話題聊起,但願能跟你一塊兒弄明白這幾個問題:咱們爲何要作性能調優?何時開始作?作性能調優是否是有標準可參考?
在互聯網項目開發中,老是不斷針對新的需求去研發新的系統,而不少系統的設計都是能夠舉一反三的,在系統架構設計中,咱們必須遵循一些原則:
海恩法則
· 事故的發生是量的積累的結果 (併發量,數據量,服務量…….)
· 再好的技術、再完美的規章, 在實際操做層面也沒法取代人自身的素質和責任心 。
墨菲定律
· 任何事情都沒有表面看起來那麼簡單 。
· 全部事情的發展都會比你預計的時間長 。
· 會出錯的事總會出錯。
· 若是你擔憂某種狀況發生,那麼它更有可能發生。
這些原則警示咱們,在互聯網公司裏,對生產環境發生的任何怪異現象和問題 都不要輕易忽視,對於其背後的緣由必定要徹查。
一樣,海恩法則也強調任何嚴重事故的背後 都是屢次小問題的積累,積累到必定的量級後會致使質變,嚴重的問題就會浮出水面 。
那麼,咱們須要對線上服務產生的任何徵兆,哪怕是一個小問題,也要刨根問底: 這就須要咱們有技術攻關的能力,對任何現象都要秉着如下原則: 爲何發生? 發生了怎麼應對? 怎麼恢復? 怎麼避免? 對問題要徹查,不能由於問題的現象不明顯而忽略 。
複製代碼
一款線上產品若是沒有通過性能測試,那它就比如是一顆定時炸彈,你不知道它何時會出現問題,你也不清楚它能承受的極限在哪兒,性能測試的目的在於驗證軟件系統是否可以達到用戶提出的性能指標,同時發現軟件系統中存在的性能瓶頸,以優化軟件。
有些性能問題是時間累積慢慢產生的,到了必定時間天然就爆炸了;而更多的性能問題是由訪問量的波動致使的,例如,活動或者公司產品用戶量上升;固然也有多是一款產品上線後就半死不活,一直沒有大訪問量,因此尚未引起這顆定時炸彈。
如今假設你的系統要作一次促銷活動,產品經理或者老闆告訴你預計有幾十萬,幾百萬,甚至更多的用戶訪問量,詢問系統可否承受得住此次活動的壓力。若是你不清楚本身系統的性能狀況,也只能戰戰兢兢地回答老闆,有可能大概沒問題吧。
因此,要不要作性能調優,這個問題其實很好回答。全部的系統在開發完以後,多多少少都會有性能問題,咱們首先要作的就是想辦法把問題暴露出來,例如進行壓力測試、模擬可能的操做場景等等,再經過性能調優去解決這些問題。
好的系統性能調優不只僅能夠提升系統的性能,還能爲公司節省資源,實現降本增效。這也是咱們作性能調優的最直接的目的。
解決了爲何要作性能優化的問題,那麼新的問題就來了:若是須要對系統作一次全面的性能監測和優化,咱們從何時開始介入性能調優呢?是否是越早介入越好?
其實,在項目開發的初期,咱們沒有必要過於在乎性能優化,這樣反而會讓咱們疲於性能優化,不只不會給系統性能帶來提高,還會影響到開發進度,甚至得到相反的效果,給系統帶來新的問題。
咱們只須要在代碼層面保證有效的編碼,同時在架構層面作好設計便可,具體架構設計能夠參考以下幾條行之有效的法則;
(1)系統的架構設計,如何在架構層面減小沒必要要的處理(網絡請求,數據庫操做等)例如:使用Cache來減小IO次數,使用異步來增長單服務吞吐量,使用無鎖數據結構來減小響應時間;
(2)網絡拓撲優化減小網絡請求時間、如何設計拓撲結構,分佈式如何實現?
(3)系統代碼級別的代碼優化,使用什麼設計模式來進行工做?哪些類須要使用單例,哪些須要儘可能減小new操做?
(4)提升代碼層面的運行效率、如何選取合適的數據結構進行數據存取?如何設計合適的算法?
(5)任務執行方式級別的同異步操做,在哪裏使用同步,哪裏使用異步?
(6)JVM調優,如何設置Heap、Stack、Eden的大小,如何選擇GC策略,控制Full GC的頻率?
(7)服務端調優(線程池,等待隊列)
(8)數據庫優化減小查詢修改時間。數據庫的選取?數據庫引擎的選取?數據庫表結構的設計?數據庫索引、觸發器等設計?是否使用讀寫分離?仍是須要考慮使用數據倉庫?
(9)緩存數據庫的使用,如何選擇緩存數據庫?是Redis仍是Memcache? 如何設計緩存機制?
(10)數據通訊問題,如何選擇通訊方式?是使用TCP仍是UDP,是使用長鏈接仍是短鏈接?NIO仍是BIO?netty、mina仍是原生socket?
(11)操做系統選取,是使用winserver仍是Linux?或者Unix?
(12)硬件配置?是8G內存仍是32G,網卡10G仍是1G? 例如:增長CPU核數如32核,升級更好的網卡如萬兆,升級更好的硬盤如SSD,擴充硬盤容量如2T,擴充系統內存如128G;
複製代碼
在系統編碼完成以後,咱們就能夠對系統進行性能測試了。這時候,產品經理通常會提供線上預期數據,咱們在提供的參考平臺上進行壓測,經過性能分析、統計工具來統計各項性能指標,看是否在預期範圍以內。
在項目成功上線後,咱們還須要根據線上的實際狀況,依照日誌監控以及性能統計日誌,來觀測系統性能問題,一旦發現問題,就要對日誌進行分析並及時修復問題。
在咱們進行調優以前,必須對性能指標有必定的認知,不然咱們調優就是空中樓閣,沒有任何的可參考的依據;是沒法實現調優的;
在咱們瞭解性能指標以前,咱們先來了解下哪些計算機資源會成爲系統的性能瓶頸,這些東西各位應該都很是清楚,在系統上線後,無外乎就是 CPU、內存、磁盤、網絡等等這些問題;
CPU:有的應用須要大量計算,他們會長時間、不間斷地佔用 CPU 資源,致使其餘資源沒法爭奪到 CPU 而響應緩慢,從而帶來系統性能問題。
例如,代碼遞歸致使的無限循環,正則表達式引發的回溯,JVM 頻繁的 FULL GC,以及多線程編程形成的大量上下文切換等,這些都有可能致使 CPU 資源繁忙。
大量線程搶佔CPU資源,致使cpu佔用率升高\CPU佔用率狀況排查:
內存:Java 程序通常經過 JVM 對內存進行分配管理,主要是用 JVM 中的堆內存來存儲 Java 建立的對象。系統堆內存的讀寫速度很是快,因此基本不存在讀寫性能瓶頸。可是因爲內存成本要比磁盤高,相比磁盤,內存的存儲空間又很是有限。因此當內存空間被佔滿,對象沒法回收時,就會致使內存溢出、內存泄露等問題。
磁盤 I/O:磁盤相比內存來講,存儲空間要大不少,但磁盤 I/O 讀寫的速度要比內存慢,雖然目前引入的 SSD 固態硬盤已經有所優化,但仍然沒法與內存的讀寫速度相提並論。
網絡:網絡對於系統性能來講,也起着相當重要的做用。若是你購買過雲服務,必定經歷過,選擇網絡帶寬大小這一環節。帶寬太低的話,對於傳輸數據比較大,或者是併發量比較大的系統,網絡就很容易成爲性能瓶頸。
異常:Java 應用中,拋出異常須要構建異常棧,對異常進行捕獲和處理,這個過程很是消耗系統性能。若是在高併發的狀況下引起異常,持續地進行異常處理,那麼系統的性能就會明顯地受到影響。
數據庫:大部分系統都會用到數據庫,而數據庫的操做每每是涉及到磁盤 I/O 的讀寫。大量的數據庫讀寫操做,會致使磁盤 I/O 性能瓶頸,進而致使數據庫操做的延遲性。對於有大量數據庫讀寫操做的系統來講,數據庫的性能優化是整個系統的核心。
鎖競爭:在併發編程中,咱們常常會須要多個線程,共享讀寫操做同一個資源,這個時候爲了保持數據的原子性(即保證這個共享資源在一個線程寫的時候,不被另外一個線程修改),咱們就會用到鎖。鎖的使用可能會帶來上下文切換,從而給系統帶來性能開銷。JDK1.6 以後,Java 爲了下降鎖競爭帶來的上下文切換,對 JVM 內部鎖已經作了屢次優化,例如,新增了偏向鎖、自旋鎖、輕量級鎖、鎖粗化、鎖消除等。而如何合理地使用鎖資源,優化鎖資源,就須要你瞭解更多的操做系統知識、Java 多線程編程基礎,積累項目經驗,並結合實際場景去處理相關問題。
瞭解了上面這些基本內容,咱們能夠獲得下面幾個指標,來衡量通常系統的性能。
響應時間是衡量系統性能的重要指標之一,響應時間越短,性能越好,通常一個接口的響應時間是在毫秒級。在系統中,咱們能夠把響應時間自下而上細分爲如下幾種:
數據庫響應時間:數據庫操做所消耗的時間,每每是整個請求鏈中最耗時的;
服務端響應時間:服務端包括 Nginx 分發的請求所消耗的時間以及服務端程序執行所消耗的時間;
網絡響應時間:這是網絡傳輸時,網絡硬件須要對傳輸的請求進行解析等操做所消耗的時間;
客戶端響應時間:對於普通的 Web、App 客戶端來講,消耗時間是能夠忽略不計的,但若是你的客戶端嵌入了大量的邏輯處理,消耗的時間就有可能變長,從而成爲系統的瓶頸。
在測試中,咱們每每會比較注重系統接口的 TPS(每秒事務處理量),由於 TPS 體現了接口的性能,TPS 越大,性能越好。在系統中,咱們也能夠把吞吐量自下而上地分爲兩種:磁盤吞吐量和網絡吞吐量。
咱們先來看磁盤吞吐量,磁盤性能有兩個關鍵衡量指標。
一種是 IOPS(Input/Output Per Second),即每秒的輸入輸出量(或讀寫次數),這種是指單位時間內系統能處理的 I/O 請求數量,I/O 請求一般爲讀或寫數據操做請求,關注的是隨機讀寫性能。適應於隨機讀寫頻繁的應用。
另外一種是數據吞吐量,這種是指單位時間內能夠成功傳輸的數據量。對於大量順序讀寫頻繁的應用,傳輸大量連續數據;
接下來看網絡吞吐量,這個是指網絡傳輸時沒有幀丟失的狀況下,設備可以接受的最大數據速率。網絡吞吐量不只僅跟帶寬有關係,還跟 CPU 的處理能力、網卡、防火牆、外部接口以及 I/O 等緊密關聯。而吞吐量的大小主要由網卡的處理能力、內部程序算法以及帶寬大小決定。
一般由 CPU 佔用率、內存使用率、磁盤 I/O、網絡 I/O 來表示資源使用率。這幾個參數比如一個木桶,若是其中任何一塊木板出現短板,任何一項分配不合理,對整個系統性能的影響都是毀滅性的。
當系統壓力上升時,你能夠觀察,系統響應時間的上升曲線是否平緩。這項指標能直觀地反饋給你,系統所能承受的負載壓力極限。例如,當你對系統進行壓測時,系統的響應時間會隨着系統併發數的增長而延長,直到系統沒法處理這麼多請求,拋出大量錯誤時,就到了極限。