先貼上優(yōu)化前后的對比圖(優(yōu)化于1月15日上線,19日上了一些細(xì)節(jié)的優(yōu)化):
-
web服務(wù)器的cpu使用
web服務(wù)器 cpu使用 -
web服務(wù)器的load
web服務(wù)器 load -
rpc的連接數(shù)(主要是與redis的連接)
rpc的連接數(shù) -
redis服務(wù)的cpu使用
redis服務(wù)的cpu使用 -
redis所在機(jī)器的連接數(shù)
redis所在機(jī)器的連接數(shù)
現(xiàn)象
- 某產(chǎn)品由于某些因素(運營,版本更新及體驗等)使得活躍用戶及停留時間不斷提高,進(jìn)而使得服務(wù)器資源使用增長,原先設(shè)定的報警閾值頻繁被觸發(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,對線上各服務(wù)(均為java進(jìn)程)取jstack并用stackAnalysis工具分析其瓶頸,發(fā)現(xiàn)許多線程都停留在對redis的操作上
- 很顯然,redis的大量請求是瓶頸
分析
- 仔細(xì)分析stack dump中有相似stackstrace的線程,發(fā)現(xiàn)對緩存的操作有不少shit的邏輯:
-
"同樣的數(shù)據(jù)取兩次"——如:interceptor中會對同一個user對象獲取兩次
e.g.: User user = userRpcServer.getUser(uid); // 一些業(yè)務(wù)邏輯 boolean isForbidderUser = userRpcServer.isForbidderUser(uid);- 從上面看并沒有直接問題,但仔細(xì)分析會發(fā)現(xiàn)userRpcServer.isForbidderUser(uid)中還會調(diào)用一次getUser(id)
-
"取到了數(shù)據(jù)卻不用"——如:獲取User對象時同時獲取其帳啟余額,但只有與帳戶相關(guān)的請求才需要獲取余額,大多數(shù)接口不需要
UserAccount userAccount = userRpcServer.getUserAccount(uid); UserInfo userInfo = userRpcServer.getUserInfo(uid); User user = makeUser(userAccount, userInfo); "已經(jīng)知道緩存中不存在數(shù)據(jù)了,卻還去取"——因為調(diào)用棧較長,所以隱藏的比較深,限于篇幅,暫不舉例。但也正因為隱藏的較深,才造成了資源的浪費不太輕易被發(fā)現(xiàn)。
-
"有大量用for循環(huán)對redis做網(wǎng)絡(luò)操作的邏輯"——如:根據(jù)userIds獲取users
for(uid : uids) { users.add(redisServer.getUser(uid)) }- 這樣的邏輯應(yīng)該盡量用批量操作的方式去完成
-
- 分析redis slow log, 找出其中耗時和頻繁的操作(尤其是刪除操作),發(fā)現(xiàn)有一些優(yōu)化的空間——此前已做過一次slow log對應(yīng)key的優(yōu)化,所以這次在這方面沒做太多事情。
解決
- 對應(yīng)上面分析到的問題,分別的解決方案為:
- 同樣的數(shù)據(jù)取兩次 —— 相同的數(shù)據(jù)只取一次
- 取到了數(shù)據(jù)卻不用 —— 不用則不取
- 已經(jīng)知道緩存中不存在數(shù)據(jù)了,卻還去取一次 —— 有的放矢
- 有大量用for循環(huán)對redis做網(wǎng)絡(luò)操作的邏輯 —— 盡量用批量請求
- redis是一個集群,因此需要用ShardedJedisPipeline來做批量請求
- 優(yōu)化之后報警短信完全消失。
優(yōu)化結(jié)果評估
- 找到優(yōu)化方法并進(jìn)行優(yōu)化之后,分別對各個優(yōu)化做性能測試
- 需要對每一步優(yōu)化都做性能測試,知悉各改動優(yōu)化的力度。
思考
- 人總是:1. 犯錯;2. 選擇最合適的辦法解決當(dāng)前需求——不論是coding還是產(chǎn)品架構(gòu)或者公司運轉(zhuǎn)。
- 錯誤總比較明顯容易解決,但隨著產(chǎn)品發(fā)展時間推移,一些曾合理的地方會變得不太合理,甚至?xí)鸬搅俗璧K作用。
- 我們需要不斷從現(xiàn)狀中找到不合理的地方,并改進(jìn)它,而不是盲從、習(xí)慣或停留于批判。