
性能優化屬于Java高級崗的必備技能,而且大廠特別喜歡考察,今天主要給大家介紹9種性能優化的方法@mikechen
1.代碼
之所以把代碼放到第一位,是因為這一點最容易引忽視,比如拿到一個性能優化的需求以后,言必稱快取、異步等,
實際上,第一步就應該是分析相關的代碼,找出相應的瓶頸,再來考慮具體的優化策略,
有一些性能問題,完全是由于代碼寫的不合理,通過直接修改一下代碼就能解決問題的,比如for回圈次數過多、作了很多無謂的條件判斷、相同邏輯重復多次等,這樣的優化成本是最低的,
2.資料庫

1.SQL優化
這里以MySQL為例,最常見的方式是,由自帶的慢查詢日志或者開源的慢查詢系統定位到具體的出問題的SQL,然后使用explain、profile等工具來逐步調優,最后經過測驗達到效果后上線,
這里舉幾個優化的例子:
1.查詢優化
對查詢進行優化,要盡量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引,
2.避免null判斷
應盡量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
3.避免全表掃描
應盡量避免在 where 子句中使用 != 或 <> 運算子,否則將引擎放棄使用索引而進行全表掃描,
應盡量避免在 where 子句中使用 or 來連接條件,如果一個欄位有索引,一個欄位沒有索引,將導致引擎放棄使用索引而進行全表掃描,
in和 not in 也要慎用,否則會導致全表掃描,如:
select id from t where num in(1,2,3)
對于連續的數值,能用 between就不要用 in 了:
select id from t where num between 1 and 3
4.大資料量查詢
對于多張大資料量的表JOIN,要先分頁再JOIN,否則邏輯讀會很高,
5.合理使用索引
索引并不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因為 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定,
一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要,
6.多使用數字型欄位
盡量使用數字型欄位,若只含數值資訊的欄位盡量不要設計為字符型,這會降低查詢和連接的性能,并會增加存盤開銷,
這是因為引擎在處理查詢和連 接時會逐個比較字串中每一個字符,而對于數字型而言只需要比較一次就夠了,
7.避免大數量
盡量避免向客戶端回傳大資料量,若資料量過大,應該考慮相應需求是否合理,
8.避免大事務
盡量避免大事務操作,提高系統并發能力,
2.連接池調優
我們的應用為了實作資料庫連接的高效獲取、對資料庫連接的限流等目的,通常會采用連接池類的方案,即每一個應用節點都管理了一個到各個資料庫的連接池,
隨著業務訪問量或者資料量的增長,原有的連接池引數可能不能很好地滿足需求,這個時候就需要結合當前使用連接池的原理、具體的連接池監控資料和當前的業務量作一個綜合的判斷,通過反復的幾次除錯得到最終的調優引數,
更加全面深入的MySQL性能優化,請查看《MySQL慢查詢優化、索引優化、以及表等優化總結》,
3.架構層面
這一類調優包括讀寫分離、多從庫負載均衡、水平和垂直分庫分表等方面,一般需要的改動較大,但是頻率沒有SQL調優高,而且一般需要DBA來配合參與,

詳細的分庫分表、讀寫分離,請查看《資料庫分庫分表、讀寫分離的原理實作,使用場景》
3.分布式快取
快取可以稱的上是性能優化的利器,快取主要用來存放那些讀寫比很高、很少變化的資料,

什么情況適合用快取?考慮以下兩種場景:
- 短時間內相同資料重復查詢多次且資料更新不頻繁,這個時候可以選擇先從快取查詢,查詢不到再從資料庫加載并回設到快取的方式,此種場景較適合用單機快取,
- 高并發查詢熱點資料,后端資料庫不堪重負,可以用快取來扛,
使用快取需要注意的問題:
1.避免快取失效
把頻繁修改的資料放入快取,容易出現資料寫入快取后,應用還來不及讀取快取,資料就已經失效的情形,徒增系統負擔,
2.快取熱點資料
快取使用的記憶體資源非常寶貴,只能將最新訪問的資料快取起來,而把歷史資料清理出快取,即快取資源應該留給20%的熱點資料,
3.資料不一致性
一般會對快取設定失效時間,超過失效時間,就要從資料庫重新加載,
因此應用要忍受一定時間的資料不一致,另一種策略是資料更新時立即更新快取,不過這也會帶來更多的系統開銷和事務一致性的問題,
4.快取可用性
業務發展到一定階段時,快取會承擔大部分資料訪問的壓力,資料庫已經習慣了有快取的日子,所以當快取服務器崩潰時,資料庫會因為完全不能承受如此大的壓力而宕機,進而導致整個網站不可用,這種情況被稱作快取雪崩,發生這種故障,甚至不能簡單地重啟快取服務器和資料庫服務器來恢復網站訪問,
解決方式:
1)快取熱備(當某臺服務器宕機時,將快取訪問切換到熱備服務器上;
2)快取服務器集群,
5. 快取預熱
快取中存放的是熱點資料,熱點資料是快取系統用LRU對不斷訪問的資料篩選出來的,這個程序需要較長的時間,
新啟動的快取系統沒有任何資料,此時系統的性能和資料庫負載都不太好,因此可以選擇在啟動快取是就把熱點資料預加載好,
6.快取穿透
因為不恰當的業務或惡意攻擊,持續高并發地訪問某一個不存在的資料,如果快取不保存該資料,就會有大量的請求壓力落在資料庫上,
簡單的解決方式是把請求的不存在的資料也放進快取,其value是null,
如果還想全面了解快取的5大難題,《如何解決Redis快取雪崩、快取穿透、快取并發等5大難題》有更加深入全面的講解,
4.異步化
針對某些客戶端的請求,在服務端可能需要針對這些請求做一些附屬的事情,這些事情其實用戶并不關心或者用戶不需要立即拿到這些事情的處理結果,這種情況就比較適合用異步的方式處理這些事情,
異步化的作用:
- 縮短介面回應時間,使用戶的請求快速回傳,用戶體驗更好,
- 避免執行緒長時間處于運行狀態,這樣會引起服務執行緒池的可用執行緒長時間不夠用,進而引起執行緒池任務佇列長度增大,從而阻塞更多請求任務,使得更多請求得不到技術處理,
- 執行緒長時間處于運行狀態,可能還會引起系統Load、CPU使用率、機器整體性能下降等一系列問題,甚至引發雪崩,異步的思路可以在不增加機器數和CPU數的情況下,有效解決這個問題,
比如:使用訊息佇列(MQ)中間件服務,MQ天生就是異步的,

一些額外的任務,可能不需要我這個系統來處理,但是需要其他系統來處理,這個時候可以先把它封裝成一個訊息,扔到訊息佇列里面,通過訊息中間件的可靠性保證把訊息投遞到關心它的系統,然后讓這個系統來做相應的處理,
再比如C端在完成一個提單動作以后,可能需要其它端做一系列的事情,但是這些事情的結果不會立刻對C端用戶產生影響,那么就可以先把C端下單的請求回應先回傳給用戶,回傳之前往MQ中發一個訊息即可,而且這些事情理應不是C端的負責范圍,所以這個時候用MQ的方式,來解決這個問題最合適,
5.Web前段
Web前端指網站業務邏輯之前的部分,包括:
- 瀏覽器加載
- 網站視圖模型
- 圖片服務
- CDN服務等

主要優化手段有優化瀏覽器訪問,使用反向代理,CDN等,
1.瀏覽器訪問優化
(1)減少http請求
HTTP協議是無狀態的應用層協議,意味著每次HTTP請求都需要簡歷通信鏈路,進行資料傳輸,而在服務器端,每個HTTP都需要啟動獨立的執行緒去處理,這些通信和服務的開銷都很昂貴,減少HTTP請求的數目可有效提高訪問性能,
減少HTTP請求的主要手段是:
- 合并CSS,以及壓縮CSS大小
- 合并JavaScript,以及壓縮JS大小
- 合并圖片
將瀏覽器一次訪問需要的JavaScript,CSS合并成一個檔案,這樣瀏覽器就只需要一次請求,多張圖片合并成一張,如果每張圖片都有不同的超鏈接,可通過CSS偏移回應滑鼠點擊操作,構造不同的URL,
(2)使用瀏覽器快取
對一個網站而言,CSS,JavaScript,Logo,圖示等這些靜態資源檔案更新的頻率都比較低,而這些檔案又幾乎是每次HTTP請求都需要的,如果將這些檔案快取在瀏覽器中,可以極好地改善性能,通過設定HTTP頭中Cache-Control和Expires屬性,可設定瀏覽器快取,快取時間可以是數天甚至是幾個月,有時候,靜態資源檔案變化需要及時應用到客戶端瀏覽器,這種情況可以通過改變檔案名實作,比如一般會在JavaScript后面加上一個版本號,使瀏覽器重繪修改的檔案,
(3)啟用壓縮
在服務器端對檔案進行壓縮,在瀏覽器端對檔案解壓縮,可有效較少通信傳輸的資料量,文本檔案的壓縮效率科大80%以上,
(4)CSS放在頁面最上面,JavaScript放在頁面最下面
瀏覽器會在下載完全部CSS之后對整個頁面進行渲染,因此最好的做法是將CSS放在頁面最上面,讓瀏覽器盡快下載CSS,JS則想法,瀏覽器在加載JS后立即執行,有可能會阻塞整個頁面,造成頁面顯示緩慢,因此JS最好放在頁面最下面,
(5)減少Cookie傳輸
一方面,Cookie包含在每次請求和回應中,太大的Cookie會嚴重影響資料傳輸,因此哪些資料需要寫入Cookie需要慎重考慮,盡量減少Cookie中傳輸的資料量,另一方面,對于某些靜態資源的訪問,如CSS,JS等,發送Cookie沒有意義,可以考慮靜態資源使用獨立域名訪問,避免請求靜態資源時發送Cookie,減少Cookie傳輸的次數,
2.CDN加速
CDN(Content Distribute Network,記憶體分發網路)的本質上仍然是一個快取,而且將資料快取在離用戶最近的地方,是用戶以最快速度獲取資料,即所謂網路訪問第一跳,
CDN一般快取的是靜態資源,如圖片,檔案,CSS,Script腳本,靜態網頁等,但是這些檔案訪問頻率很高,將其快取在CDN可極大改善網頁的打開速度,
3.反向代理
傳統代理服務器位于瀏覽器一側,代理瀏覽器將HTTP請求發送到互聯網上,而反向代理服務器位于網站機房一側,代理網站Web服務器接收HTTP請求,
和傳統代理服務器可以保護瀏覽器安全一樣,反向代理服務器也具有保護網站安全的作用,來自互聯網的訪問請求必須經過代理服務器,相當于在Web服務器和可能的網路攻擊之間建立了一個屏障,
除了安全功能,代理服務器也可以通過配置快取功能加速Web請求,當用戶第一次訪問靜態內容的時候,靜態內容就被快取在反向代理服務器上,這樣當其他用戶訪問該靜態內容的時候,就可以直接從反向代理服務器回傳,加速Web請求回應速度,減輕服務器負載要,
6.服務化
做服務化最基礎的是按業務做服務拆分,避免跨業務間的互相影響,資料和服務同時拆分,同一個業務內部我們還按計算密集型/IO密集型的服務拆分、C端/B端服務拆分、核心/非核心服務拆分、高頻服務單獨部署等原則做拆分,
7.硬體升級
硬體問題對性能的影響不容忽視,
舉一個例子:一個DB集群經常有慢SQL報警,業務排查下來發現SQL都很簡單,該做的索引優化也都做了,后來DBA同學幫忙定位到問題是硬體過舊導致,將機械硬碟升級成固態硬碟之后報警立馬消失了,效果立竿見影!
8.搜索引擎
復雜查詢以及一些聚合計算不適合在資料庫中做,可以利用搜索引擎來實作,另外搜索引擎還可以幫我們很好的解決跨庫、跨資料源檢索的場景,
9.產品邏輯優化
業務邏輯優化經常會容易被忽略,但效果卻往往比資料庫性能優化、JVM調優之類的來的更明顯,
舉一個例子,12306春運搶火車票的場景,由于訪問的人多,用戶點擊“查票”之后系統會非常卡,進度條非常慢,作為用戶,我們會習慣性的再去點“查票”,可能會連續點個好幾次,
假設平均一個用戶點5次,則后端系統負載就增加了5倍!而其中80%的請求是重復請求,
這個時候我們可以通過產品邏輯的方式來優化,比如,在用戶點擊查詢之后將“按鈕置灰”,或者通過JS控制xx秒只能只能提交一次請求等,有效的攔截了80%的無效流量,
以上
作者簡介
陳睿|mikechen,10年+大廠架構經驗,《BAT架構技術500期》系列文章作者,分享十余年BAT架構經驗以及面試心得!
閱讀mikechen的互聯網架構更多技術文章合集
Java并發|JVM|MySQL|Spring|Redis|分布式|高并發|架構師
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/502038.html
標籤:架構設計
上一篇:企業應用架構研究系列二十三:微服務之程式網關YARP
下一篇:設計模式——創建型設計模式
