春節(jié)初一~初三期間,紅包活動(dòng)頁左上角會(huì)有春節(jié)排行榜的入口,前期預(yù)估峰值:15w/s。
排行榜server優(yōu)化前后數(shù)據(jù)對(duì)比如下(備注:以下數(shù)據(jù),均假設(shè)從CKV中拿到數(shù)據(jù),忽略掉oidb相關(guān)邏輯):
測(cè)試條件:CPU占比不超過85%,超時(shí)時(shí)間不超過900ms
| 優(yōu)化前單機(jī)QPS | 優(yōu)化后單機(jī)QPS | |
|---|---|---|
| 獲取排行榜首頁數(shù)據(jù)(實(shí)時(shí)拉取好友步數(shù)并進(jìn)行排序) | 2200 | 5200 ( +3000) |
| 獲取排行榜分頁數(shù)據(jù)(從Redis快照中直接獲取數(shù)據(jù)) | 3000 | 5500 ( +2500) |
| 用戶點(diǎn)贊 | 12000 | 12000 |
| 發(fā)送C2C消息 | 12000 | 12000 |
針對(duì)春節(jié)排行榜運(yùn)動(dòng)側(cè)準(zhǔn)備了120臺(tái)V8機(jī)器。以排行榜首頁為例,優(yōu)化前,系統(tǒng)QPS(極限值) = 2200*120 = 26.4w/s,優(yōu)化后,系統(tǒng)QPS(極限值) = 5200*120 = 62.4w/s,提升136.4%。
以CPU占比75%平穩(wěn)運(yùn)行而言,系統(tǒng)QPS = 4700*120 = 56.4w/s,此時(shí)平均每個(gè)請(qǐng)求從發(fā)包到收包耗時(shí)約40ms。

所有接口中,由于排行榜首頁,做的邏輯更多更復(fù)雜(包括拉取關(guān)系鏈、取所有好友步數(shù)、排序、取用戶信息、取會(huì)員標(biāo)記、取點(diǎn)贊數(shù)據(jù)等),因此,不出意外,首頁QPS是最低的。另外,春節(jié)期間排行榜所有請(qǐng)求中,首頁的請(qǐng)求應(yīng)該是最多的,畢竟,用戶進(jìn)入頁面首先就會(huì)請(qǐng)求首頁數(shù)據(jù)。
綜上所述,優(yōu)化排行榜首頁性能成為了整個(gè)系統(tǒng)的關(guān)鍵所在。這次優(yōu)化,也是根據(jù)這條思路進(jìn)行的,下面簡(jiǎn)要介紹下優(yōu)化的思路和過程。
一、排行榜邏輯架構(gòu)圖

要點(diǎn)如下:
- 存儲(chǔ):主要采用CKV,
- 外部接口:能異步就異步(除oidb查會(huì)員標(biāo)記位外)
- 框架:SPP微線程,相關(guān)網(wǎng)絡(luò)操作均采用異步。
- 備注:SSO尋址走h(yuǎn)ash一致性尋址,server本地采用Redis做快照,防止排名錯(cuò)亂的問題。
二、壓測(cè)數(shù)據(jù)
工欲善其事,必先利其器。這里不得不提到test.server.com這個(gè)壓測(cè)利器,文中所有數(shù)據(jù)和結(jié)果均來自test.server.com提供的壓測(cè)客戶端。
優(yōu)化的步驟無外乎:
A. 壓測(cè)得到當(dāng)前Server性能(CPU不超過80%,延遲不超過900ms)
B. Perf查看CPU消耗點(diǎn)在哪里(因?yàn)榕判邪襁@里是CPU Bound型)
C. 針對(duì)B的結(jié)果相應(yīng)優(yōu)化,再重復(fù)進(jìn)行步驟A。
1. 原始?jí)簻y(cè)數(shù)據(jù)(未做優(yōu)化前):壓測(cè)到2200/s時(shí),CPU占比已經(jīng)飆升至80%。


分析后得知,Server中的Json和map的相關(guān)操作吃了很大一部分CPU,約占用30%左右。
2. 優(yōu)化map操作,將代碼中所有涉及到map的邏輯全部替換為hash_map和vector。思路:map底層采用RB_Tree的方式實(shí)現(xiàn),查找復(fù)雜度為OLog(n);hash_map底層采用Hash_table實(shí)現(xiàn),查找復(fù)雜度為O(1)。
| 優(yōu)化前QPS | 優(yōu)化后QPS | 優(yōu)化措施 |
|---|---|---|
| 2200 | 3200 (+1000) | hash_map和vector替換map操作 |
備注:C99里面的hash_map,不是標(biāo)準(zhǔn)庫(kù),是gcc實(shí)現(xiàn)的:__gnu_cxx::hash_map。這里性能提升明顯,主要是獲取步數(shù)數(shù)據(jù)、用戶關(guān)系鏈數(shù)據(jù)及相互匹配時(shí),查找操作較多。
優(yōu)化后Perf圖如下(搜索關(guān)鍵字<map>如下圖,CPU占比11%左右):
右圖中,之所以map匹配CPU占比超過11%,因?yàn)镴son相關(guān)的操作中大量的使用了map,而Json還沒有進(jìn)行優(yōu)化。搜索hash_map,CPU占比只有5%左右。
3. 優(yōu)化Json操作。思路:將Json庫(kù)替換為簡(jiǎn)單字符串拼接(這里由于是和前端交互,限定了只能使用json交互,因此優(yōu)化的思路還是從組裝json方面考慮,而不是替換為二進(jìn)制協(xié)議,例如pb等)。
| 優(yōu)化前QPS | 優(yōu)化后QPS | 優(yōu)化措施 |
|---|---|---|
| 3200 | 4000 (** +800**) | json組裝,改為自己拼接字符串 |


右圖中,可以看到,優(yōu)化完成后,占用CPU最多的已經(jīng)變?yōu)閟sdasn::的相關(guān)操作,這些操作是CKV存儲(chǔ)的編解碼封裝,也就是說,后續(xù)的性能優(yōu)化已經(jīng)和業(yè)務(wù)無關(guān)了。
此時(shí)CPU使用如下,占比約80%左右:
4. 減少日志流水操作。思路:忽略正常處理的請(qǐng)求,只打印出錯(cuò)請(qǐng)求處理日志。
| 優(yōu)化前QPS | 優(yōu)化后QPS | 優(yōu)化措施 |
|---|---|---|
| 4000 | 4500 (+500) | 減少日志打印,只打印出錯(cuò)日志 |



5. gcc編譯增加O2選項(xiàng)。思路:利用編譯器自身的編譯優(yōu)化選項(xiàng),從編譯執(zhí)行的底層嘗試優(yōu)化性能(業(yè)務(wù)無關(guān))
| 優(yōu)化前QPS | 優(yōu)化后QPS | 優(yōu)化措施 |
|---|---|---|
| 4500 | 5200 (** +700**) | 使用gcc O2優(yōu)化選項(xiàng) |



三、走過的彎路
在初步壓測(cè)出單機(jī)QPS=2200后,由于缺乏經(jīng)驗(yàn),沒有從火焰圖去分析CPU到底消耗在哪里。因此只能嘗試各種經(jīng)驗(yàn)上的方法進(jìn)行優(yōu)化,例如:
- 去掉同步快照操作(傷害用戶體驗(yàn))
- 去掉關(guān)系鏈CKV同步操作(更換成本高)
- 調(diào)整批量獲取CKV的數(shù)量(經(jīng)驗(yàn)值)
- 調(diào)大進(jìn)程數(shù)
- 關(guān)閉系統(tǒng)日志
- 關(guān)閉排序(排行榜基礎(chǔ)功能)
等...
這些效果都不明顯,提升作用有效,最多的時(shí)候,有損體驗(yàn)的前提下,QPS才提升到2500左右。
后面在查閱相關(guān)資料后,系統(tǒng)化的使用perf、火焰圖等工具進(jìn)行分析,抓到性能瓶頸后,有的放矢,才能在后面的優(yōu)化過程中,有效的提升系統(tǒng)QPS。
這次優(yōu)化,從接觸學(xué)習(xí)壓測(cè)工具開始,到昨天優(yōu)化告一段落,斷斷續(xù)續(xù)持續(xù)了有3、4天左右。
參考資料
gcc 編譯優(yōu)化選項(xiàng)O2相關(guān)知識(shí),可以參考這篇文章GCC中-O1 -O2 -O3 優(yōu)化的原理
另外,O2選項(xiàng)優(yōu)化打開時(shí),注意另外一個(gè)選項(xiàng):-fno-strict-aliasing,如果沒有配合使用的話,程序可能產(chǎn)生未定義的行為,大意是說O2選項(xiàng)默認(rèn)認(rèn)為不同類型的指針不能指向同一片內(nèi)存區(qū),如果業(yè)務(wù)代碼中,有強(qiáng)制類型轉(zhuǎn)換,需要注意下這里。具體參考:gcc 編譯參數(shù) -fno-strict-aliasing
官方說明在此:Options That Control Optimization
我的理解:O2性能優(yōu)化選項(xiàng)打開前后代碼語義必然相同,最主要的性能優(yōu)化點(diǎn),可能還是:未打開前,Gcc編譯生成的代碼是獨(dú)立的,每一行代碼都可以打斷,方便調(diào)試;打開后,Gcc編譯生成的代碼是相關(guān)的,并根據(jù)一些相關(guān)性進(jìn)行了優(yōu)化,當(dāng)然這時(shí)候,調(diào)試的難度就很大了。
