一次簡單通用的壓測調優實戰
目的:提升單個介面的QPS(每秒查詢率QPS是對一個特定的查詢伺服器在規定時間內所處理流量多少的衡量標準。)
工具:本文不關注工具的使用,簡單起見,使用apache的 ab
工具
步驟:先測試並優化一個空介面(直接返回請求的介面),拿到一個極限QPS,然後分別引進Redis、MySQL的介面分別進行測試並優化,使其儘量接近極限QPS。步驟嚴格遵循控制變數法。
環境:為得到儘量接近真實生產環境的資料,需要提前建立一個與生產環境幾乎一致的壓測環境,在壓測環境上進行測試。當前環境為:AWS EC2上部署的K8S叢集,共2個Node節點,CPU為2核。
空介面壓測優化
獲取資料
先在程式碼中寫上一個直接返回請求的test介面,然後部署到容器叢集中作為被壓測的物件,再登入到另外一個作為施壓機進行壓測。
同時注意檢測CPU,記憶體,網路狀態,因為對於一個空介面,QPS達到上限一定是因為該服務所在宿主機的某處效能達到瓶頸。
命令: ab -n 3000 -c 300 "http://10.1.2.3/test/"
測試環境:
Requests per second:1448.86 [#/sec] (mean) Time per request:69.020 [ms] (mean) Connection Times (ms) minmean[+/-sd] medianmax Connect:111.118 Processing:56629.463164 Waiting:56629.463164 Total:56729.764165 複製程式碼
在整個壓測過程中,使用 top -d 0.3
發現CPU達到瓶頸,為驗證我們的猜想,將相同的服務部署到另外一個CPU效能更好的機器上測試。
CPU效能提升之後的本地環境:
Requests per second:3395.25 [#/sec] (mean) Time per request:29.453 [ms] (mean) Connection Times (ms) minmean[+/-sd] medianmax Connect:000.703 Processing:22813.32684 Waiting:22813.32684 Total:22913.42686 複製程式碼
定位問題
Processing time的解釋:The server response time—i.e., the time it took for the server to process the request and send a reply
結論很明顯,在Connect連線時長几乎一致的情況下,伺服器端處理時間大大減少,所以 CPU 對QPS的影響是最直接的。
另外,依據經驗判斷,2核CPU的機器本地壓測只有3k是不正常的一個數據,所以猜測框架效能有差異,所以對不同框架進行了空介面的測試,資料如下:
beego: 12k , go micro: 3.5k, spring boot: 5.5k
結論是 框架 直接大幅度影響效能,分析一下,go micro與beego、spring boot的區別是它是一個 微服務 架構的框架,需要consul、api gateway、後臺服務一起啟動,一個請求來到gateway之後,可能需要查詢consul拿到後臺服務的地址,還需要將JSON格式轉換為gRPC格式傳送給後臺服務,然後接收後臺服務的返回,這直接導致了在CPU計算量、網路傳輸量最少兩倍於單體應用,即使go micro的閘道器和後臺服務分開在兩臺機器上部署,測試之後QPS也只能到達5.5k。
我是直接感受到了一個微服務的缺點:相比於單體架構服務的直接返回請求,微服務架構服務的開銷是很可能更大的,所以在選擇架構的時候需要在效能與微服務帶來的優勢(服務解耦、職責單一、獨立開發構建部署測試)上進行衡量,當然如果你伺服器夠多效能夠好,當我沒說。
進行優化
在CPU無法提升、框架無法改變的情況下,只能在框架和服務的配置、程式碼的使用、架構層面進行優化。
1
參考go micro作者給的方法,對框架的配置進行優化:
--client_pool_size=10 # enables the client side connection pool
再次在測試環境下測試,QPS提升300,到達1700。
2
api gateway是所有流量都會走的地方,所以是優化的重要部分,我們先測試一下刪掉其中的業務程式碼,QPS提升了600,到達2300,所以優化了一下這裡的邏輯,QPS到達2100
if !strings.Contains(cookies, "xxx") { return nil, errors.New("no xxx cookie found") } 複製程式碼
替換掉
cookiesArr := strings.Split(cookies, ";") for _, v := range cookiesArr { cookie := strings.Split(v, "=") if strings.Trim(cookie[0], " ") == "xxx" { sid = cookie[1] break } } 複製程式碼
3
最後,使用k8s的自動伸縮機制,另外部署了一組api gateway和後臺服務到不同的Node上,相當於配置好了兩臺機器的負載均衡,QPS達到3200
Redis相關介面優化
找到一個僅包含一次Redis get操作且無返回資料的介面作為測試介面,在空介面優化之後的基礎上進行測試,QPS:2000
對於連線另外的一個服務或中介軟體的情況,優化的方式並不多,最常用的優化方式就是提升連線數,在服務內部將連線Redis的連線數提MaxIdle提升到800,MaxActive提升到10000,QPS提升500,達到2500。
另外可以檢視Redis服務的一些配置和效能圖示,當前環境下使用的是AWS上的Redis服務,可配置的專案不多,使用的是兩個節點的配置,在壓測的過程中檢視CPU佔用、連線數佔用、記憶體佔用,均未發現達到上限,所以Redis服務沒有可以優化的餘地。
資料庫相關介面優化
在高併發場景下,為了儘量提升QPS,查詢的操作應該儘量全部使用Redis做快取代替或者將請求儘量攔截在查詢資料庫之前,所以資料庫的查詢操作並不是QPS提升關注的重點。
但需要對資料庫進行一些通用的優化,比如主從複製,讀寫分離、提升連線數、在大資料量下分表、優化SQL、建立索引。下面以一個查詢操作為例,一條複雜的sql為例做一次SQL優化與索引建立,以單次的查詢時間為目標進行優化。
準備資料
先用儲存過程準備50w條資料
DELIMITER $$ CREATE PROCEDURE prepare_data() BEGIN DECLARE i INT DEFAULT 1; WHILE i < 500000 DO INSERT INTO game_record (app_id, token, game_match_id, user_id, nickname, device_id, prize_grade, start_time, score) VALUES ('appid', 'abc', concat('xx',i), '70961908', 'asdfafd', 'xcao', 2, 1543894842, i ); SET i = i + 1; END WHILE; END$$ DELIMITER ; call prepare_data() 複製程式碼
建立索引
如何選擇合適的列建立索引?
- WHERE / GROUP BY / ORDER BY / ON 的列
- 離散度大(不同的資料多)的列使用索引才有查詢效率提升
- 索引欄位越小越好,因為資料庫按頁儲存的,如果每次查詢IO讀取的頁越少查詢效率越高
對於以下SQL:
select * from game_record where app_id = ? and token=? and score != -1 order by prize_grade, score limit 50 複製程式碼
優化前時間: 0.551s
對於order by操作,可以建立複合索引:
create index grade_and_score on game_record(prize_grade, score) 複製程式碼
優化後時間: 0.194s
優化SQL
這裡不貼具體的SQL語句了,以下是一些SQL通用優化方式:
- 使用精確列名查詢而不是*,特別是當資料量大的時候
- 減少子查詢,使用Join替代
- 不用NOT IN,因為會使用全表掃描而不是索引;不用IS NULL,NOT IS NULL,因為會使索引、索引統計和值更加複雜,並且需要額外一個位元組的儲存空間。
還有一些具體的資料庫優化策略可以參考 這裡
TODO
以上是僅僅是排除程式碼之後的服務和中介軟體壓測,還應該加入程式碼邏輯進行更加全面的測試,然後對程式碼進行優化,具體優化方式請參考對應程式語言的優化方式。
如果以後碰到效能瓶頸,擴機器是最簡單高效的,或者更換其他框架,或則還可以深入優化一下api gateway和後臺服務互動的資料傳輸效能,因為這塊是直接導致CPU達到瓶頸的原因。
總結
如果想做好一次壓測和優化,需要非常清晰的思路、高效的壓測方法、排查問題的套路、解決問題的方案,但最基礎的還是需要知道一個請求從瀏覽器傳送之後,到返回到瀏覽器,之間到底經歷過什麼,這篇比較基礎的文章可以幫你瞭解一些,但還不夠,比如這次調優中還涉及到資料庫優化、Go語言、硬體效能、AWS、Docker、K8S這樣的雲平臺和容器技術、容器編排工具,所以壓測調優是一次對自己掌握服務端整個架構和細節的考驗和學習過程。
91Code-就要編碼,關注公眾號獲取更多內容!
