如何排查系統(tǒng)的性能瓶頸點?
不點藍字,我們哪來故事?


梳理系統(tǒng)的性能瓶頸點這件事應該不是一件簡單的事情,需要針對不同設計的系統(tǒng)來進行單獨分析。
首先一套完整可用的系統(tǒng)應該是有ui界面的(這里強調的是一套完整的,可用的系統(tǒng),而并不是指單獨的一個中臺系統(tǒng)),系統(tǒng)分為了前端模塊和后端模塊。
這里由于我個人的擅長領域更多是處于后端模塊,所以對于系統(tǒng)的瓶頸點梳理我會從后端進行分析。
這里我結合常用的nginx+tomcat+redis+mysql這類常見架構進行分析:

請求入口 所有的請求打入到后臺的服務當中,首先需要考慮的一個點就是:
帶寬因素:
假設有200m的流量同時請求進入服務器,但是帶寬只有1m,這么來算光是接收這批數據量信息也要消耗大約200s的時間。帶寬可以理解為在指定時間內從一端請求到另一端的流量總量。而且局域網和廣域網的帶寬計算其實也是不一樣的,
服務器的ulimit
通常我們使用的線上服務器都是centos系列,這里我列舉centos7相關的系統(tǒng)配置:ulimit配置 查看服務器允許的最大打開文件數目(linux系統(tǒng)中設計概念為一切皆文件) 通常如果我們的java程序需要增大一些socket的鏈接數目,可以通過調整ulimit 里面的open參數進行配置。
?[root@izwz9ic9ggky8kub9x1ptuz?~]#?ulimit?-a?|?grep?open
open?files??????????????????????(-n)?1000
查看用戶的最大進程數目
[root@izwz9ic9ggky8kub9x1ptuz?~]#?ulimit?-a?|?grep?user
max?user?processes??????????????(-u)?7284
相關的配置存放在了/etc/security/limits.conf文件中。
系統(tǒng)的一些內核參數配置
如果是在一些壓力測試場景中,我們通常會預見到這種報錯:
apr_socket_recv:?Connection?reset?by?peer?(54)
通常這種情況是因為系統(tǒng)內部的一些防范參數設置導致的,需要調整/etc/sysctl.conf 文件中的相關參數:
net.ipv4.tcp_syncookies?=?0
#當并發(fā)請求數目超過了1000之后,服務器自身可能會認為是收到了syn泛洪攻擊,但對于高并發(fā)系統(tǒng),要禁用此設置
?
net.ipv4.tcp_max_syn_backlog
#參數決定了SYN_RECV狀態(tài)隊列的數量,一般默認值為512或者1024,即超過這個數量,系統(tǒng)將不再接受新的TCP連接請求,一定程度上可以防止系統(tǒng)資源耗盡。可根據情況增加該值以接受更多的連接請求。
?
net.ipv4.tcp_tw_recycle
#參數決定是否加速TIME_WAIT的sockets的回收,默認為0。
?
net.ipv4.tcp_tw_reuse
#參數決定是否可將TIME_WAIT狀態(tài)的sockets用于新的TCP連接,默認為0。
??
net.ipv4.tcp_max_tw_buckets
#參數決定TIME_WAIT狀態(tài)的sockets總數量,可根據連接數和系統(tǒng)資源需要進行設置。
對于防范參數還可以如下修改查看:
cd?/proc/sys/net/ipv4
echo?"0"?>?tcp_syncookie
通常企業(yè)中使用的都是nginx進行接收請求,然后進行負載均衡轉發(fā)。在nginx層里面會有幾個核心參數配置:最大連接數,最大并發(fā)訪問數
#指定同一個ip的每次請求數量都限制為10次
limit_conn_zone?$binary_remote_addr?zone=perip:10m;
limit_conn?perip?10
Tomcat部分分析
Tomcat支持三種接收請求的處理方式:BIO、NIO、APR 。
1、Bio方式,阻塞式I/O操作即使用的是傳統(tǒng)Java I/O操作,Tomcat7以下版本默認情況下是以bio模式運行的,由于每個請求都要創(chuàng)建一個線程來處理,線程開銷較大,不能處理高并發(fā)的場景,在三種模式中性能也最低
2、Nio方式,是Java SE 1.4及后續(xù)版本提供的一種新的I/O操作方式(即java.nio包及其子包),是一個基于緩沖區(qū)、并能提供非阻塞I/O操作的Java API,它擁有比傳統(tǒng)I/O操作(bio)更好的并發(fā)運行性能。tomcat 8版本及以上默認nio模式
3、apr模式,簡單理解,就是從操作系統(tǒng)級別解決異步IO問題,大幅度的提高服務器的處理和響應性能, 也是Tomcat運行高并發(fā)應用的首選模式。啟用這種模式稍微麻煩一些,需要安裝一些依賴庫, 而apr的本質就是使用jni技術調用操作系統(tǒng)底層的IO接口,所以需要提前安裝所需要的依賴,首先是需要安裝openssl和apr
tomcat連接參數調整
在tomcat中有這么一段經典的配置參數:
"80"?maxHttpHeaderSize="8192"
????maxThreads="4000"?minSpareThreads="1000"?maxSpareThreads="2000"
????enableLookups="false"?redirectPort="8443"?acceptCount="2000"
????connectionTimeout="20000"?disableUploadTimeout="true"?/
maxThreads表示tomcat最多可以創(chuàng)建多少個線程來處理請求。
minSpareThread表示tomcat一開始啟動的時候會創(chuàng)建多少個線程,即使是閑著也會創(chuàng)建。
maxSpareThread表示tomcat創(chuàng)建的最大閑置線程數目。一旦tomcat創(chuàng)建的線程數目達到這個瓶頸,那么就需要進行線程的回收了。
connectionTimeout表示連接的超時時長。
假設我們同時有1000個請求并發(fā)訪問,但是一臺tomcat的maxThreads只設置為了500,那么此時就會出現(xiàn)請求擁塞的情況,也就是瓶頸點之一。
Redis部分性能瓶頸分析
一些大key的查詢,導致網絡出現(xiàn)擁塞情況
例如說往一個list集合中存儲了50m的數據,一旦發(fā)生list全量查詢,同時又有其他指令在進行訪問的時候,就容易會導致網絡堵塞。因為redis的設計為單線程處理請求,所以其他指令發(fā)送到redis服務端的時候,都需要等待redis將之前的任務處理完畢之后才能繼續(xù)執(zhí)行。
線上環(huán)境出現(xiàn)了一些”違規(guī)操作“
比較常見的違規(guī)操作:批量執(zhí)行keys指令
在redis處于高qps的狀態(tài)下,隨意一個keys指令都可能是致命的。keys指令的時間復雜度是O(n)級別,容易導致一時間系統(tǒng)的卡頓。
內存空間不足
當redis處于內存空間不足的時候,基本就是整個系統(tǒng)處于癱瘓作用。因此我們在對每個存儲在redis中的數值都需要設置一個合理的過期時間,以及需要思考存儲數據的體積大小。
MySQL部分性能瓶頸分析
通常我們在分析sql查詢方面都容易出現(xiàn)一個誤區(qū),就是上來直接進行explian分析,但是卻忽略了系統(tǒng)的運作上下文環(huán)境。
假設有一張t_user表,已經存儲了幾千萬的數據,并且也對用戶的id進行了索引建立,但是sql執(zhí)行速度依舊是超過1s時長,這個時候就需要換一種思路進行分析了。
例如從表的拆分方面進行思考,是否該對表進行橫向拆分,拆解為t_user_01,t_user_02......
以下是我總結的一些對于數據庫層面可能出現(xiàn)性能瓶頸的幾點總結:
1.鎖
排查是否會存在鎖表的情況導致數據庫響應緩慢。
2.sql查詢還有優(yōu)化空間,有待完善
通常我們對于sql的執(zhí)行分析都會使用explain命令進行查看:
這里我貼出了一張關于explain的常用參數含義表供大家參考:

3.查詢出的數據量過大
例如說一條sql直接查詢了全表的數據信息量,直接占滿了網絡帶寬,因此訪問時候出現(xiàn)了網絡擁塞。
4.硬件設備不足
例如在面對一些高qps的查詢時候,數據庫本身的機器硬件配置較低,自然處理速度會比較慢。
5.自適應hash出現(xiàn)鎖沖突
AHI是innodb存儲引擎特有的屬性,innodb存儲引擎會針對索引數據的查詢結果做自適應的優(yōu)化,當某些特定的索引查詢頻率特別高的時候會自動為其建立hash索引,從而提升查詢的效率。相比于B+Tree索引來說,hash索引能夠大大減少對于io的訪問次數,“一擊命中” 查詢數據,具備更加高效的性能,而且hash索引是由mysql內部自動適配的,無需dba在外部做過多的干預。
早期版本的hash索引是采用了單鎖模式來防范并發(fā)訪問問題,這對于程序自身的一個運作高效性有一定的”折扣“,后期通過對hash索引進行了分區(qū),不同頁的數據用不同的hashtable,每個分區(qū)有對應的鎖來做并發(fā)訪問的預防。
如果某天你發(fā)現(xiàn)了有很多線程都被堵塞在了RW-latches的時候,有可能就是因為hash索引的并發(fā)訪問負載過高導致的堵塞,這個時候可以通過增大hash索引的分區(qū)參數,或者關閉自適應hash索引特性來進行處理。
往期推薦
下方二維碼關注我

技術草根,堅持分享?編程,算法,架構


