先貼上優(yōu)化前后的對(duì)比圖(優(yōu)化于1月15日上線,19日上了一些細(xì)節(jié)的優(yōu)化):
-
web服務(wù)器的cpu使用
-
web服務(wù)器的load
-
rpc的連接數(shù)(主要是與redis的連接)
-
redis服務(wù)的cpu使用
-
redis所在機(jī)器的連接數(shù)
現(xiàn)象
- 某產(chǎn)品由于某些因素(運(yùn)營,版本更新及體驗(yàn)等)使得活躍用戶及停留時(shí)間不斷提高野揪,進(jìn)而使得服務(wù)器資源使用增長乾蓬,原先設(shè)定的報(bào)警閾值頻繁被觸發(fā)拴魄,具體表現(xiàn)為:
- web機(jī)器的load與cpu使用都變高
- 依賴的緩存服務(wù)redis連接數(shù)與cpu不斷攀升
- rpc(用于業(yè)務(wù)分離)的連接數(shù)不斷攀升
如何解決
- 負(fù)載均衡谤狡,增加服務(wù)器——最簡單粗暴且有效的方式
- 找到系統(tǒng)性能瓶頸虫溜,優(yōu)化之——這是最經(jīng)濟(jì)也最有挑戰(zhàn)的事
- ——考慮到線上服務(wù)尚未出現(xiàn)功能性問題游桩,先嘗試第二種方式
定位
- 參考我之前的blog牲迫,對(duì)線上各服務(wù)(均為java進(jìn)程)取jstack并用stackAnalysis工具分析其瓶頸,發(fā)現(xiàn)許多線程都停留在對(duì)redis的操作上
- 很顯然借卧,redis的大量請(qǐng)求是瓶頸
分析
- 仔細(xì)分析stack dump中有相似stackstrace的線程盹憎,發(fā)現(xiàn)對(duì)緩存的操作有不少shit的邏輯:
-
"同樣的數(shù)據(jù)取兩次"——如:interceptor中會(huì)對(duì)同一個(gè)user對(duì)象獲取兩次
e.g.: User user = userRpcServer.getUser(uid); // 一些業(yè)務(wù)邏輯 boolean isForbidderUser = userRpcServer.isForbidderUser(uid);
- 從上面看并沒有直接問題,但仔細(xì)分析會(huì)發(fā)現(xiàn)userRpcServer.isForbidderUser(uid)中還會(huì)調(diào)用一次getUser(id)
-
"取到了數(shù)據(jù)卻不用"——如:獲取User對(duì)象時(shí)同時(shí)獲取其帳啟余額铐刘,但只有與帳戶相關(guān)的請(qǐng)求才需要獲取余額陪每,大多數(shù)接口不需要
UserAccount userAccount = userRpcServer.getUserAccount(uid); UserInfo userInfo = userRpcServer.getUserInfo(uid); User user = makeUser(userAccount, userInfo);
"已經(jīng)知道緩存中不存在數(shù)據(jù)了,卻還去取"——因?yàn)檎{(diào)用棧較長镰吵,所以隱藏的比較深檩禾,限于篇幅,暫不舉例疤祭。但也正因?yàn)殡[藏的較深盼产,才造成了資源的浪費(fèi)不太輕易被發(fā)現(xiàn)。
-
"有大量用for循環(huán)對(duì)redis做網(wǎng)絡(luò)操作的邏輯"——如:根據(jù)userIds獲取users
for(uid : uids) { users.add(redisServer.getUser(uid)) }
- 這樣的邏輯應(yīng)該盡量用批量操作的方式去完成
-
- 分析redis slow log, 找出其中耗時(shí)和頻繁的操作(尤其是刪除操作)勺馆,發(fā)現(xiàn)有一些優(yōu)化的空間——此前已做過一次slow log對(duì)應(yīng)key的優(yōu)化戏售,所以這次在這方面沒做太多事情侨核。
解決
- 對(duì)應(yīng)上面分析到的問題,分別的解決方案為:
- 同樣的數(shù)據(jù)取兩次 —— 相同的數(shù)據(jù)只取一次
- 取到了數(shù)據(jù)卻不用 —— 不用則不取
- 已經(jīng)知道緩存中不存在數(shù)據(jù)了灌灾,卻還去取一次 —— 有的放矢
- 有大量用for循環(huán)對(duì)redis做網(wǎng)絡(luò)操作的邏輯 —— 盡量用批量請(qǐng)求
- redis是一個(gè)集群搓译,因此需要用ShardedJedisPipeline來做批量請(qǐng)求
- 優(yōu)化之后報(bào)警短信完全消失。
優(yōu)化結(jié)果評(píng)估
- 找到優(yōu)化方法并進(jìn)行優(yōu)化之后锋喜,分別對(duì)各個(gè)優(yōu)化做性能測試
- 需要對(duì)每一步優(yōu)化都做性能測試些己,知悉各改動(dòng)優(yōu)化的力度。
思考
- 人總是:1. 犯錯(cuò)嘿般;2. 選擇最合適的辦法解決當(dāng)前需求——不論是coding還是產(chǎn)品架構(gòu)或者公司運(yùn)轉(zhuǎn)段标。
- 錯(cuò)誤總比較明顯容易解決,但隨著產(chǎn)品發(fā)展時(shí)間推移博个,一些曾合理的地方會(huì)變得不太合理怀樟,甚至?xí)鸬搅俗璧K作用。
- 我們需要不斷從現(xiàn)狀中找到不合理的地方盆佣,并改進(jìn)它往堡,而不是盲從、習(xí)慣或停留于批判共耍。