MySQL Insert語句單個批次數量過多導致的CPU性能問題分析
【問題】
最近有臺服務器比較頻繁的CPU報警,表現的特征有CPU sys占比偏高,大量慢查詢,大量並發線程堆積。後面開發對insert的相關業務限流後,服務器性能恢復正常。
【異常期間線程處理情況】
下圖是當時生產環境異常時抓取的信息,該事務正在執行insert,已經執行5秒,線程運行在innodb內核,狀態是thread declared inside InnoDB,還有4906 tickets可用
統計了下有64個線程在innodb層,同時看到還有280個線程在排隊等待進入innodb線程,狀態是sleeping before entering InnoDB
innodb層的並發線程執行的SQL比較慢,產生了阻塞,導致了mysql的並發線程堆積。
【哪些SQL執行慢】
從正在執行的SQL中,看到了insert的慢查詢SQL語句,統計了下這句SQL批量插入大於342條記錄(SQL被截斷)
【批量insert的性能測試】
類似這種批量的insert SQL會對MySQL性能造成影響嗎,多大的批次比較合理呢,做了下面測試
在測試服務器上新建測試表(表結構同生產環境),並定義了5個插入腳本,分別為單條insert,每10條1個批次insert,每50條1個批次insert,每100條1個批次insert,每340條1個批次insert
用壓測工具模擬512個並發線程的情況下,不同類型的SQL插入100W條記錄服務器的性能情況,下表是壓測統計
|
數據量 |
並發線程 |
執行時間(秒) |
每秒insert |
慢查詢數量 |
Context switch |
CPU使用率 |
CPU sys占比 |
普通insert(1條) |
1000000 |
512 |
33 |
3W |
0 |
79W |
73% |
39% |
批量SQL(10條) |
1000000 |
512 |
23 |
4.3W |
0 |
49W |
78% |
56% |
批量SQL(50條) |
1000000 |
512 |
21 |
4.7W |
0 |
42W |
80% |
60% |
批量SQL(100條) |
1000000 |
512 |
15 |
6.6W |
20 |
53W |
70% |
45% |
批量SQL(340條) |
1000000 |
512 |
15 |
6.6W |
200 |
38W |
86% |
70% |
下圖是批量SQL(340條)執行時的性能情況,可以看到當每100條記錄一個批次執行insert時,開始出現慢查詢,每340條1個批次執行insert時,在高並發的情況下,會產生大量的慢查詢,這個現象接近於我們目前生產環境異常時的情況
【優化方案】
對於MySQL需要插入大量數據時,每次單條的insert性能較差,為了提升insert性能,我們采用了每批次多條記錄同時insert的方法。
但當批次增大到一定數量時,在高並發訪問的情況下,單個批次執行的性能會出現較大的下降,出現大量慢查詢,並發線程堆積,CPU上升出現瓶頸, innodb層的並發線程處理被慢查詢阻塞,後面只能通過限流來緩解性能問題。
根據上面的測試結論,建議控制熱表單個批次insert的記錄條數,最好單個批次控制在10條左右(因為即使調大到50條,插入性能沒有大的提升,在高並發場景下,首先要保證當前SQL的執行性能)。
【優化後CPU告警消失,運行平穩】
MySQL Insert語句單個批次數量過多導致的CPU性能問題分析