代碼
之所以把代碼放到第一位,是因為這一點最容易引起技術人員的忽視。很多技術人員拿到一個性能優化的需求以后,言必稱緩存、異步、JVM等。實際上,第一步就應該是分析相關的代碼,找出相應的瓶頸,再來考慮具體的優化策略。有一些性能問題,完全是由於代碼寫的不合理,通過直接修改一下代碼就能解決問題的,比如
for循環次數過多、作了很多無謂的條件判斷、相同邏輯重復多次等。
數據庫
數據庫的調優,總的來說分為以下三部分:
SQL調優
這是最常用、每一個技術人員都應該掌握基本的SQL調優手段(包括方法、工具、輔助系統等)。這里以MySQL為例,最常見的方式是,由自帶的慢查詢日志或者開源的慢查詢系統定位到具體的出問題的SQL,然后使用explain、profile等工具來逐步調優,最后經過測試達到效果后上線。這方面的細節,可以參考MySQL索引原理及慢查詢優化。
架構層面的調優
這一類調優包括讀寫分離、多從庫負載均衡、水平和垂直分庫分表等方面,一般需要的改動較大,但是頻率沒有SQL調優高,而且一般需要DBA來配合參與。那么什么時候需要做這些事情?我們可以通過內部監控報警系統(比如Zabbix),定期跟蹤一些指標數據是否達到瓶頸,一旦達到瓶頸或者警戒值,就需要考慮這些事情。通常,DBA也會定期監控這些指標值。
連接池調優
我們的應用為了實現數據庫連接的高效獲取、對數據庫連接的限流等目的,通常會采用連接池類的方案,即每一個應用節點都管理了一個到各個數據庫的連接池。隨着業務訪問量或者數據量的增長,原有的連接池參數可能不能很好地滿足需求,這個時候就需要結合當前使用連接池的原理、具體的連接池監控數據和當前的業務量作一個綜合的判斷,通過反復的幾次調試得到最終的調優參數。
異步
使用場景
針對某些客戶端的請求,在服務端可能需要針對這些請求做一些附屬的事情,這些事情其實用戶並不關心或者用戶不需要立即拿到這些事情的處理結果,這種情況就比較適合用異步的方式處理這些事情。
作用
- 縮短接口響應時間,使用戶的請求快速返回,用戶體驗更好。
- 避免線程長時間處於運行狀態,這樣會引起服務線程池的可用線程長時間不夠用,進而引起線程池任務隊列長度增大,從而阻塞更 多請求任務,使得更多請求得不到技術處理。
- 線程長時間處於運行狀態,可能還會引起系統Load、CPU使用率、機器整體性能下降等一系列問題,甚至引發雪崩。異步的思路可 以在不增加機器數和CPU數的情況下,有效解決這個問題。
常見做法
一種做法:
是額外開辟線程,這里可以采用額外開辟一個線程或者使用線程池的做法,在IO線程(處理請求響應)之外的線程來處理相應的任務,在IO線程中讓response先返回。
如果異步線程處理的任務設計的數據量非常巨大,那么可以引入阻塞隊列BlockingQueue作進一步的優化。具體做法是讓一批異步線程不斷地往阻塞隊列里扔數據,然后額外起一個處理線程,循環批量從隊列里拿預設大小的一批數據,來進行批處理(比如發一個批量的遠程服務請求),這樣進一步提高了性能。
另一種做法:
是使用消息隊列(MQ)中間件服務,MQ天生就是異步的。一些額外的任務,可能不需要我這個系統來處理,但是需要其他系統來處理。這個時候可以先把它封裝成一個消息,扔到消息隊列里面,通過消息中間件的可靠性保證把消息投遞到關心它的系統,然后讓這個系統來做相應的處理。
比如C端在完成一個提單動作以后,可能需要其它端做一系列的事情,但是這些事情的結果不會立刻對C端用戶產生影響,那么就可以先把C端下單的請求響應先返回給用戶,返回之前往MQ中發一個消息即可。而且這些事情理應不是C端的負責范圍,所以這個時候用MQ的方式,來解決這個問題最合適。
多線程與分布式
使用場景
離線任務、異步任務、大數據任務、耗時較長任務的運行,適當地利用,可達到加速的效果。
注意:線上對響應時間要求較高的場合,盡量少用多線程,尤其是服務線程需要等待任務線程的場合(很多重大事故就是和這個息息相關),如果一定要用,可以對服務線程設置一個最大等待時間。
常見做法
如果單機的處理能力可以滿足實際業務的需求,那么盡可能地使用單機多線程的處理方式,減少復雜性;反之,則需要使用多機多線程的方式。
對於單機多線程,可以引入線程池的機制,作用有二:
提高性能,節省線程創建和銷毀的開銷
限流,給線程池一個固定的容量,達到這個容量值后再有任務進來,就進入隊列進行排隊,保障機器極限壓力下的穩定處理能力在使用JDK自帶的線程池時,一定要仔細理解構造方法的各個參數的含義,如core pool size、max pool size、keepAliveTime、worker queue等,在理解的基礎上通過不斷地測試調整這些參數值達到最優效果
轉載:
作者:你是妖怪吧
鏈接:https://www.jianshu.com/p/f57d05247329
來源:簡書