線上接口突然變慢優(yōu)化方案

1、 背景

某一天早上,正在上班路上,突然間手機(jī)滴滴不斷收到大量告警提醒,趕緊查看了下告警信息,結(jié)果顯示某個(gè)接口出現(xiàn)大量超時(shí),平均響應(yīng)時(shí)間超過3s,這個(gè)時(shí)候怎么辦,是不是有點(diǎn)慌?

2、解決思路

出現(xiàn)生產(chǎn)問題,必須要找到根本原因及時(shí)處理,防止下次留下更大的坑。

快速定位問題

首先我們要快速定位接口的哪一個(gè)環(huán)節(jié)比較比較慢,性能瓶頸在哪里?

這個(gè)時(shí)候可以采用APM工具快速定位,常見的工具:skywalking、pinpoint、zipkin。假如我們應(yīng)用沒有接入APM,可以在生產(chǎn)環(huán)境裝一下阿里的Arthas,利用trace 接口 方法,大概能分析是哪一塊比較慢,定位的力度稍微有點(diǎn)粗糙。阿里開源Java診斷工具 Arthas 使用

解決辦法

  • 擴(kuò)容(應(yīng)用自動(dòng)擴(kuò)容、redis擴(kuò)容、mysql在線擴(kuò)容、kafka分區(qū)擴(kuò)容)

    首先要保證系統(tǒng)正常,所以如果是系統(tǒng)瓶頸,那我們可以做應(yīng)用擴(kuò)容;如果是redis節(jié)點(diǎn)cpu/內(nèi)存使用率高,可以做redis擴(kuò)容;如果是因?yàn)槁齭ql導(dǎo)致myql 扛不住了,也可以在線擴(kuò)容mysql;kafka同理,如果是kafka消息積壓了,那可以分區(qū)擴(kuò)容;

    簡(jiǎn)單說就是先讓通過擴(kuò)容使系統(tǒng)暫時(shí)恢復(fù)正常,不至于引起更嚴(yán)重的問題

  • 應(yīng)用重啟大法

    擴(kuò)容只能使新的應(yīng)用新的節(jié)點(diǎn)保持正常,但是對(duì)于已有的CPU 100%的節(jié)點(diǎn),那可以通過重啟,釋放資源

  • 優(yōu)化代碼邏輯,走h(yuǎn)otfix發(fā)版解決

    前面的都是應(yīng)急的辦法,在定位到對(duì)應(yīng)代碼后,終歸是要通過優(yōu)化代碼來發(fā)版處理的。

3、常見優(yōu)化接口性能方案分析

1. 數(shù)據(jù)庫(kù)慢SQL

通過explain執(zhí)行計(jì)劃分析下

  • 未加索引,加索引時(shí)對(duì)于一些大表,不要在業(yè)務(wù)高峰期加,不然容易會(huì)鎖表;

  • 加了索引,索引失效(對(duì)索引加方法轉(zhuǎn)換、區(qū)分度很低比如枚舉值、索引列大量空值)

  • 鎖表(先把鎖表的慢SQL kill一波)

  • 小表驅(qū)動(dòng)大表(盡可能添加過濾條件)

  • SOL太雜(jon超過3張表或者子查詢比較多,建議拆分SQL為多個(gè)接口,比如先從某個(gè)主接口查某個(gè)表數(shù)據(jù),然后關(guān)聯(lián)字段作為條件從另外一個(gè)表查詢,進(jìn)行內(nèi)存拼接)

  • 返回的數(shù)據(jù)量數(shù)據(jù)量太大(可以分頁(yè)多批次查詢,管理端可以考慮多線程查詢,C端高并發(fā)不建議多線程查詢)

  • 單表數(shù)據(jù)量太大(考慮放分片庫(kù)或分表或者clickhouse、es存儲(chǔ))

    MySQL Explain詳解 InnoDB索引底層原理

2. 調(diào)用第三方接口慢

  • 調(diào)用第三方設(shè)置合理的超時(shí)時(shí)間,比如你的接口是高并發(fā)接口,從自身對(duì)方接口的要求和對(duì)方線上P95接口的平均rt,綜合設(shè)置超時(shí)時(shí)間,超時(shí)時(shí)間應(yīng)該大于對(duì)方線上P95接口的平均rt;

  • 集成sentinel或hystrix限流熔斷框架,防止對(duì)方接口拖垮我們自己的接口;

  • 事務(wù)型操作根據(jù)實(shí)際的情況決定是否重試補(bǔ)償(本地消息表+jb重試),比如新增、修改等操作要考慮對(duì)方接口是否支持冪等,防止超發(fā);

  • 循環(huán)調(diào)用,改為單次批量調(diào)用,減少IO損耗;

  • 緩存查詢結(jié)果(比如根據(jù)用戶ID查詢用戶信息)

3. 中間件慢

  • redis慢:是否有熱key、大key;

    熱key: 本地緩存

    大key: 拆分大key或者采用set結(jié)構(gòu)的sismember等方法判斷-0(1)時(shí)間復(fù)雜度

    Redis(三):常見異常及解決方案

  • kafka慢:

    生產(chǎn)端慢:向kafka丟消息慢了,可以使用阻塞隊(duì)列接收,批量發(fā)送消息等優(yōu)化

    消費(fèi)端慢:擴(kuò)分區(qū)、增加消費(fèi)節(jié)點(diǎn)、增加消費(fèi)線程或批量消費(fèi)

4. 程序邏輯慢

  • 非法校驗(yàn)邏輯前置,避免無(wú)用數(shù)據(jù)穿透消耗系統(tǒng)資源,減少無(wú)效調(diào)用;

  • 循環(huán)調(diào)用改為單次調(diào)用,比如查數(shù)據(jù)庫(kù)或查其他rpc或restful接口,能批量調(diào)用盡量批量調(diào)用,數(shù)據(jù)在內(nèi)存組裝處理;

  • 同步調(diào)用改為異步調(diào)用 (采用CompletableFuture異步非阻塞,并行調(diào)用不同的rpc接口);

    Java異步任務(wù)編排—CompletableFuture(一) Java異步任務(wù)編排—CompletableFuture(二)

  • 非核心邏輯剝離(拆分大事務(wù),采用MQ異步解耦);

  • 線程池合理設(shè)置(千萬(wàn)不要?jiǎng)?chuàng)建無(wú)界隊(duì)列線程池,線程池滿了以后要重寫拒絕策略,考慮告警加數(shù)據(jù)持久化);

  • 鎖合理設(shè)置(本地讀寫鎖設(shè)計(jì)不合理或鎖力度太大、分布式鎖合理使用防止熱點(diǎn)key);

  • 優(yōu)化gc參數(shù)(考慮young gcfull gc是否太頻繁、調(diào)整c算法、新生代老年代比例);

  • 只打印必要日志(warn或error級(jí)別)

5. 架構(gòu)優(yōu)化

  • 高并發(fā)讀邏輯都走redis,盡可能不穿透到db;

  • 涉及寫邏輯數(shù)據(jù)(異步、批量處理、分庫(kù)分表);

  • 接口接入限流熔斷兜底(sentinel或hystrix);

  • 監(jiān)控告警(error日志告警、接口慢查詢或不可用告警,限流熔斷告警、DB告警、中間件告警、應(yīng)用系統(tǒng)告警);

  • 接口加動(dòng)態(tài)配置開關(guān)快速切斷流量或降級(jí)某一些非核心服務(wù)調(diào)用;

  • 設(shè)計(jì)自動(dòng)對(duì)賬job,保證數(shù)據(jù)自動(dòng)可修復(fù);

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容