压测导致mysql数据库CPU很高_排查压测问题引发的系统性能调优过程
前言:今天測試部門的小夢找到我,委屈巴巴的說我寫的接口有問題,因為她對這個接口進行壓力測試時,發現系統的吞吐量一直上不去,并且 應用服務器 (部署接口項目的服務器) 的CPU、內存等資源的使用率也一直很低,導致一直無法測試出這個接口的壓力峰值。
聽小夢說完后,自己心想接口都測試了好幾遍了,接口代碼 絕對不可能有問題的,再說了,有問題也不能承認呀,看來得往別的地方上扯扯呀;然后我說道,接口應該是沒問題的,可能是項目環境部署時有些參數沒進行調優吧,例如:連接數大小設置、JVM參數設置、數據庫參數優化等;
然后我接著說道,項目是誰給你部署配置的呀,小夢說是小王部署的,然后今天小王也沒來上班;我說道,小王沒來上班呀,沒事,讓我來,我這bug絕緣體質,任何問題遇到我都會退避三舍的。
上面的情景,我相信大家都可能會遇到過的;接下來我們就通過這次排查壓測問題來聊聊一個 單體系統 的性能優化應該考慮哪些點,以及對這些點該怎么進行調優 。
本文主線如下圖:
項目部署環境:在進行這次壓測問題排查前,先通過下圖了解下 接口項目的情況、項目部署的環境、JMeter測試腳本 的配置情況;
上圖中的接口項目、Redis、MySql 都是單機安裝部署的 。
注意: 再進行下文之前,我們還需要記住一個前提:本項目中的接口代碼是不存在問題的,并且數據庫查詢SQL都是最優的,都是走索引查詢的,本次壓力測試問題不是由于代碼導致的,而是由于各種參數未調優造成的;代碼調優和SQL調優在編碼階段就已經完成了。
簡單性能調優的點:在上文中的項目部署情況和測試腳本的配置情況下進行壓力測試時,就出現了小夢說的系統吞吐量上不去,以及應用服務器(部署接口項目的服務器)的 CPU 等資源使用率都很低的情況;
查看 JMeter 測試時系統的吞吐量如下圖:
查看應用服務器(部署接口項目的服務器)的CPU使用率很低:
按照測試腳本的50個并發以及接口中含有計算密集型操作(加解密和驗簽)的情況,CPU是不應該這么低的;哎!什么鬼嘛?
別急,下面我們就展開具體的排查過程,并在排查過程中逐步了解各個調優的點;
排查過程就是按照文章開頭中的 本文主線 圖片中 簡單性能調優的點 展開的 。
排查Tomcat連接器參數配置:
Tomcat 連接器參數配置只需要考慮以下幾個方面:Connector使用哪種 protocol 協議HTTP/1.1:默認值,使用的協議與Tomcat版本有關
建議使用 NIO2:org.apache.coyote.http11.Http11Nio2Protocol
除了NIO2協議外,還有 BIO、NIO、APR 協議,可以自行去查閱資料;
acceptCount:等待隊列的長度;當等待隊列中連接的個數達到acceptCount時,說明隊列已滿,再進來的請求一律被拒絕;默認值是100。
maxConnections:Tomcat 在任意時刻接收和處理的最大連接數;當連接數達到最大值maxConnections后,Tomcat會繼續接收連接,直到accept等待隊列填滿。
如果最大連接數設置為-1,則表示禁用maxconnections功能,表示不限制tomcat容器的連接數;
最大連接數默認值與連接器使用的 protocol 協議有關:NIO的默認值是10000;
APR/native的默認值是8192;
BIO的默認值為maxThreads(如果配置了Executor,則默認值是Executor的maxThreads);
maxThreads:每一次HTTP請求到達 Tomcat,都會創建一個線程來處理該請求,那么最大線程數決定了Tomcat 可以同時處理多少個請求。maxThreads 默認200;
建議:maxThreads應該設置大些,以便能夠充分利用CPU;當然,也不是越大越好,如果maxThreads過大,那么CPU會花費大量的時間用于線程的上下文切換,整體效率可能會降低;這需要根據自己項目的情況和服務器硬件配置情況配置合適的值即可;
下面就是本次壓測時配置的Tomcat連接器參數:
port="8080"
protocol="org.apache.coyote.http11.Http11Nio2Protocol"
maxThreads="1000"
maxConnections="2000"
acceptCount="1000"
connectionTimeout="20000"
redirectPort="8443"
enableLookups="false"
maxPostSize="10485760"
compression="on"
disableUploadTimeout="true"
compressionMinSize="2048"
acceptorThreadCount="2"
compressableMimeType="text/html,text/xml,text/css,text/javascript"
URIEncoding="utf-8" />
上面連接器中配置參數:的連接器使用的 protocol 協議是 NIO2
最大線程數 maxThreads="1000"
最大連接數 maxConnections="2000"
等待隊列大小 acceptCount="1000"
根據上面連接器配置參數,和壓測腳本配置的并發請求才50個,確定壓測問題應該不是由于連接器參數造成的。
其實在查看Tomcat配置文件 server.xml 中的連接器參數前,可以使用下面這個命令先直接統計出 JMeter與Tomcat建立的Http連接數: 注意 8080 指的是 Tomcat 端口號,使用前改成你的Tomcat配置的端口netstat -pan | grep 8080 | wc -l
如果統計出的連接數與測試腳本中配置的并發數的話差不多一致的話,說明當前連接器是可以滿足連接請求處理的,初步可以判斷不是由于連接器參數造成的本次壓測問題;然后再可以去查看配置文件中的參數進行確認下。
排查 JVM 參數配置:JVM參數配置,一般并發不高的系統只需要配置下Heap堆內存的大小即可,其余使用默認配置即可。
注意:JVM 參數配置比較復雜,這塊的參數調優需要根據具體項目情況、服務器硬件配置等進行合理配置。
查看當前壓測的接口項目 JVM 配置參數:直接使用命令查看:ps -ef | grep tomcat
結果如下:
也可以直接去 catalina.sh 配置文件中查看:JAVA_OPTS=" -server -Xms6000m -Xmx6000m "也可以直接通過 jmap 命令查看 JVM 的堆的配置信息:
注意:下面命令中的 129761 是tomcat的 pid 進程號;jmap -heap 129761
結果如下:
看到這,應該會有人問,為什么你的初始堆大小和最大堆大小(-Xms6000m -Xmx6000m)設置為一樣呢?
因為如果虛擬機啟動時設置的初始堆內存比較小,這個時候又需要初始化很多對象數據,那么虛擬機就必須不斷地擴大堆內存,這樣也會產生一些消耗的。
排查下是否由于JVM參數配置的不合理導致:使用上面提到的 jmap 命令查看下當前堆內存的使用情況jmap -heap 129761
通過查看上面的堆內存的使用情況,發現還有很多空間使用呢,應該不是由于JVM參數不合理導致的壓測問題;
但是咱別著急,咱再查看下JVM中垃圾回收GC的情況;使用命令 jstat 查看垃圾回收GC的情況:
注意:129761 為Tocamt的pid; 1000 表示間隔時間毫秒,10 表示輸出10次GC情況jstat -gc 129761 1000 10
看了上面的GC情況,發現垃圾回收也沒什么異常,FGC總共就3次,通過查看的堆使用情況和GC垃圾回收的情況可以確認壓測的問題不是由于JVM的參數導致的。
看來這次壓測的問題挺棘手呀,到現在還沒有確認出問題原因呢!難道我這bug絕緣體質由于 今天吃的比較多,導致失效了嗎? 皺眉 ing . . . . .
看來還得接著排查,咱直接導出當前接口服務應用進程的 線程堆棧信息 看看吧。使用命令jstack導出線程棧信息:
注意:129761 為Tomcat 的pid, /usr/local/test.log 為生成的線程堆棧文件 test.log 的存放地址jstack 129761 >> /usr/local/test.log
導出線程堆棧信息后,使用命令搜索出狀態為 WAITING 的線程;
注意:找 State 為 WAITING 狀態的線程;
命令如下:test.log 文件為剛剛導出的 線程堆棧信息文件cat test.log | grep -n20 " WAITING "
在命令執行后的搜索結果中發現了下圖內容:
注意:
除了線程狀態為 WAITING 的之外,還特別要注意 WAITING 狀態線程的方法調用棧中存在項目業務代碼的;上面圖片中就展示了線程狀態為 WAITING 等待的調用方法棧中有業務代碼;
業務代碼是去Redis中查詢數據,上圖中第二個圈中的內容就是業務代碼,并且這行代碼上方是去Redis連接池中獲取連接,由此確定是由于Redis連接獲取不到,導致線程處于 WAITING 等待狀態;
由于接口服務系統中很多線程獲取不到Redis連接導致阻塞,從而無法充分利用CPU;所以在壓測時,服務器CPU的使用率也壓不上去,CPU的使用率很低;至此,終于排查出了一處問題地方,不容易呀,感覺排查問題比寫代碼困難多了!
上面通過線程的堆棧信息排查出 Redis 連接不夠用,應該是 Redis 連接池中的最大連接數配置的比較小,那接下來就確定下連接池的配置數據。
排查連接池參數配置:
上文中,在線程的堆棧信息中排查出存在很多線程由于獲取不到Redis連接導致處于等待阻塞狀態;
接下來,咱們就來具體統計下現在處于壓測中的應用服務器(部署接口項目的服務器)與Redis建立的連接數,順帶一起再排查下項目中使用的MySql 的連接池配置是否也存在問題?使用命令查看當前壓測中的應用服務器與Redis建立的連接數
注意:6379 為 redis的端口號netstat -pan | grep 6379 | wc -l
執行命令查詢出與Redis建立的連接數為 10,這在測試腳本50個并發請求下是一定不夠用的呀,應該是配置的Redis連接池的最大連接數比較小;
然后通過看接口代碼中的配置文件發現,確實Redis連接池中的最大連接數配置是 10 個;最后修改了最大連接數為 300 個;注意:這個最大連接數需要根據項目情況和測試計劃來設置,不用設置的太大。接著,使用命令順帶統計下當前處于壓測中的應用服務器(部署接口項目的服務器)與MySql 建立的連接數
注意:3306 為MySql 的端口號netstat -pan | grep 3306 | wc -l
執行命令發現已經建立的MySql 連接數是 50,然后通過查看配置文件的連接池中最大的連接數為100,發現還沒有達到最大連接數,說明 JDBC 連接夠用,本次壓測問題不是由于JDBC連接不夠用導致的;并且在上文中查看線程的堆棧信息時,也沒有發現調用 JDBC 操作的線程處于 WAITING 等待狀態。
排查完了數據庫連接池了,咱們就再排查下MySql 數據庫是否進行了參數調優;
如果MySql 數據庫沒有進行調優的話,那在壓測時就會出現數據庫的讀寫性能比較差,也就是項目中在進行 JDBC 增刪改查操作時會比較慢,進而導致存在JDBC操作的線程執行慢,無法充分利用CPU的并發計算能力,所以也會導致應用服務器的CPU使用率比較低,最終壓測時整個接口應用系統的吞吐量也很低 。
排查MySql 數據庫調優配置:接下來咱排查下MySql 數據庫是否進行了參數調優;
排查前先聊一個由于之前MySql 數據庫未進行優化,而導致的壓測問題。
MySql 數據庫未調優,引發的壓測問題 簡述:
之前壓測時的場景還原:
在壓測前所有該調優的地方都調了,就單獨忘記對MySql 數據庫進行調優了,最終導致對應用壓測時,發現應用服務器(部署接口項目的服務器)的CPU的使用率一直浮動在60%左右,始終壓不上去,即使加大壓測時的并發請求數,發現還是不行;
最終通過查看MySql 服務器的磁盤IO的情況和MySql 數據庫進程的CPU、內存等使用率發現MySql 沒有被優化,所以導致MySql 數據庫的讀寫能力被限制,進而導致應用并發處理能力下降,從而導致應用服務器的CPU使用率一直升不上去。使用命令統計下壓測時 MySql 數據庫所部署在的服務器的磁盤IO性能:
注意: 1 指的是 間隔時間1秒, 5 指的是統計5次 IO 的情況;iostat -xdk 1 5
結果如下:
只需要注意圖中圈起來的地方;await 指的是 磁盤讀寫時的等待時間,這個值越小越好;%util 指的是磁盤整體的負載情況,值越大說明磁盤負載快達到極限了。使用命令查看MySql 進程的CPU、內存的使用率:
注意: 14208 是MySql 的pid;top -p 14208
結果如圖:
注意:
當時就是通過這兩條命令統計出的數據,發現數據庫服務器磁盤IO的負載一直處于90%,快達到極限了;但是MySql 進程的CPU、內存的使用率卻很低;
通過這兩條數據可以初步確定MySql 沒有進行優化;如果MySql 進行了調優,那么MySql 的進程在壓測時的CPU、內存等使用率不可能這么低,并且磁盤 IO 的負載也不會達到了極限;
得出初步結論后,然后去查看MySql 數據庫的配置文件 my.cnf, 發現確實沒有進行一些參數的優化,只是簡單配置了最大連接數為 500;注意,MySql 數據庫默認的最大連接數是 100 個;
最后在 my.cnf 配置文件中簡單配置了下 innodb 存儲引擎緩沖池大小 等;這里就不具體描述調優的參數了,因為調優參數都是需要根據項目情況、服務器硬件配置等綜合考慮配置的;大家可以自行網上查閱MySql 數據庫調優的相關資料;
為什么配置下 innodb 存儲引擎緩沖池大小 等參數就會極大的提升數據庫的讀寫性能呢?
通過下面這張圖片就會知道為什么了:
通過上圖可以了解到進行數據庫讀寫時,首先都在對內存中的innodb存儲引擎的緩沖池中的數據進行操作,如果緩沖池中不存在操作的數據,則需要進行磁盤IO將數據讀取到內存中的,所以這樣讀寫速度就會慢很多;
所以如果緩沖池比較小的話,無法存放很多數據(數據頁)的話,則需要進行磁盤IO(磁盤IO很耗時的),那么數據庫讀寫性能會比較差,所以需要對MySql 的存儲引擎的緩存池等參數進行調優配置。
配置好后,重啟了MySql 數據庫,然后重新進行壓測,發現應用服務器(部署接口項目的服務器)的CPU使用率上去了,MySql 數據庫進程的CPU、內存等使用率也升上去了,并且磁盤IO的負載也降下來了,最后整個應用系統的吞吐量也上去了,也支持更大的并發數了。說完了上面之前遇到的一個壓測小場景,咱們接著按照上面的排查步驟排查這次壓測的問題,發現MySql 數據庫已經進行了調優,磁盤IO的讀寫速度也是OK的,所以說本次壓測問題與MySql 數據庫參數配置無關。
排查完了MySql 數據庫后,咱接著排查下Linux服務器的參數是否進行過調優吧!
排查Linux服務器參數配置:服務器參數調優這塊目前了解的不多,只知道配置下服務器的 文件句柄數 fd ;建議將服務器的文件句柄數設置的大些;為什么設置大些呢?
因為主流操作系統的設計是將 TCP/UDP 連接采用與文件一樣的方式去管理,即一個連接對應于一個文件描述符(文件句柄);
主流的 Linux 服務器默認所支持最大 文件句柄數 數量為 1024,當并發連接數很大時很容易因為 文件句柄數 不足而出現 “open too many files” 錯誤,導致新的連接無法建立。
建議將 Linux 服務器所支持的最大句柄數調高數倍(具體與服務器的內存數量相關)。使用命令查看當前服務器的文件句柄數:ulimit -a
結果如圖:
上圖中的文件句柄數是已經修改過的;設置文件句柄數可以設置臨時值,也可以設置永久值,建議設置成永久值,因為臨時值遇到一些情況會失效的。
配置文件句柄數可以自行去網上查詢,資料很多的,本文就不做過多描述了。聊完服務器參數調優配置后,咱最后再聊聊 JMeter 測試工具在壓測時有沒有該注意的點!
JMeter 分布式測試:
這里主要聊聊 JMeter 分布式測試的內容;什么時候需要進行分布式測試,當你的單機 JMeter 服務器由于硬件配置限制無法構建出足夠的并發壓力時,這是就需要進行分布式部署測試了;
怎么判斷 單機的 JMeter 服務器 已經無法構建出足夠的并發壓力了呢?通過查看 JMeter 測試服務器的CPU的使用率情況,當 JMeter 測試服務器的CPU使用率達到 90% 多時,此時就可以初步確認CPU無法在提供足夠的計算能力來構建并發壓力了;
所以此時可以將單機的JMeter改為分布式JMeter進行測試,只有構建出足夠的壓力,才能將應用服務器(部署接口項目的服務器)的CPU、內存等資源的使用率壓上去,從而測試出接口的壓力峰值。所以,當單機 JMeter 壓測時發現應用服務器(部署接口項目的服務器)CPU、內存等使用率壓不上去的話,也可以去排查下是否是由于JMeter服務器硬件配置不足造成的;
本次壓力問題,發現 JMeter 測試服務器在壓測時 CPU 的使用率達到了 80%多,說明此臺測試服務器由于硬件限制也是無法構建更大的并發壓力了,所以建議將單機 JMeter 測試改為 分布式測試;
這里 JMeter 分布式測試網上教程很多,本文就不做過多描述了。
到這里,本人認為的所有該調優的點都排查完; 今啊老百姓真呀真呀真高興!
本次排查總結:
排查完上面的幾個點后,最終發現了導致本次壓測問題的原因:Redis 連接池的最大連接數配置的偏小
JMeter 單機測試無法構造出足夠的并發壓力
正是由于上面的兩個問題導致了本次壓測出現了下面的問題:壓測時應用系統的吞吐量偏低
并且應用服務器(部署接口項目的服務器)的CPU、內存等使用率偏低;將上面的兩處問題修改過后,重新進行壓測,發現一切正常了!
在此,再說明下本次壓測項目就是一個簡單的單體項目,項目中沒有涉及到分庫分表、各種中間件以及分布式等,所以排查起來還相對簡單些 ,否則問題排查會更加困難的。
由于本人水平有限,如果有未提及到的調優的點,請評論留言呀!
擴展
在排查問題時,如果有一些 排查工具 的話,將極大的方便我們排查問題,從而使我們快速的定位到問題原因!
下面就介紹兩個在排查問題非常好用的輔助工具:Arthas : 阿里開源的線上Java應用診斷工具
jvisualvm :JDK中自帶的 JVM 運行狀態監控工具
? 點贊 + 評論 + 轉發 喲
如果本文對您有幫助的話,請揮動下您愛發財的小手點下贊呀,您的支持就是我不斷創作的動力,謝謝!
您可以VX搜索【木子雷】公眾號,大量Java學習干貨文章,您可以來瞧一瞧喲!
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的压测导致mysql数据库CPU很高_排查压测问题引发的系统性能调优过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php mysql_query的的用法_
- 下一篇: mysql utf8转gbk cmd_转