接口響應過慢的原因排查


最近一次的項目體驗,手機用戶在訓練完成之后,會有服務器超時的提示,在用戶量大的時候,每晚的7-9點時間段發生的尤為頻繁,所以作了一些的排查。

排查的順序乃是這樣的:

  1. 確定是哪個接口存在性能問題
  2. 確定這個接口的內部邏輯是怎樣的,做了哪些事情
  3. 分析接口存在性能問題的根本原因
  4. 尋找確立優化方案
  5. 回歸驗證方案效果

首先哪個接口的話,代碼當中馬上就找到了,立刻開始確定里面的邏輯,這一下就發現了問題了,里面的邏輯隨着業務的增加,這里面變得越來越臃腫,包括對多張表的查詢、幾張表的更新與新增,這一下就看到了,可是里面的一些邏輯實在太難拆出來了,上下的連接性太嚴重,需要前面的數據,后面的業務才能繼續的走下去。沒辦法,就看看還有什么其他的地方需要優化的。

  1.在sql日志的打印中,喜不自勝的看到了一連串了slow 慢sql語句,這下子可以從這些sql語句上着手的,這是肯定要優化的,都是一些執行時間大於1s的,更有甚者到達了5-6s。

 

 

 

 最嚴重的,我發現這是一條update語句,竟然不是select查詢語句,后來查了一下的資料,發現可能是因為事務的原因,線程被阻塞,鎖住了要查詢的表,導致sql語句過慢,接口也就相對應的慢起來,發現這個update的語句,會對播放記錄play_total多次的進行+1的操作,每次當前視頻video被跳一次,它的熱度就作+1處理,導致這個字段被更新的過於頻繁,這張表被鎖死了,其他對video的查詢操作進行不下去,整個接口的響應被拖慢了。

解決方案:將這個play_total字段單獨拿出來做張副表,專門對這個字段作更新+1的操作,這樣在頻繁也不會,對其他的查詢操作有影響了,尤其是對video的影響

 

 

 PS:其實還有更好的方法,對於這種頻繁操作,但是卻不太重要的字段,可以直接存在緩存當中,在服務器啟動時,做一次查詢操作,后面所有的更新操作直接在緩存里進行,這樣也就不會對數據庫有任何的影響了。隔段時間作同步操作,將緩存的數據同步到數據庫中,這個涉及到數據的同步,可能會增加復雜度,使用的話,慎重。

  2.在作了這些操作之后,當晚發布后,第二天再次到達晚上用戶量較多時,發現情況稍有好轉,但是問題仍舊存在,有一些其他接口的響應時長在30s以上,用戶還是時常會報服務器超時,於是再次排查問題所在,又再次發現了,問題接口里的邏輯寫的有些問題,很多的for循環里嵌套了一些其他的一些實現類,然后里面包含對數據庫的操作,這樣也就相當於循環多少次,會建立多少次的連接,嚴重拖慢了接口的響應。

 

 

 

 於是作了一些的操作,在for循環外面將所有數據查詢出來,sql里面用in  foreach標簽,這2個sql作了聯表查詢,

   3.還有最后一個問題就是,后台會經常報“斷開的管道”這個異常,實在是不經常見到的錯誤,所以在網上搜了一些的答案

 

 

 https://blog.csdn.net/zqz_zqz/article/details/52235479

在這里面的文章里找到一些有用的內容,原來是因為接口響應過慢,導致前端那邊設置的超時時間到了,直接主動切斷了與后端的連接,但是后端還在查數據,查完以后想返給前端,發現管道已經斷掉了,就只好報這個錯誤的

 

 后來又找了一些其他的資料,終於找到了解決方案,

 

 

 

 

解決方法

1、調大防火牆的連接切斷時長#

這是一個臨時解決方法,比如將防火牆的連接超時時間調整為8小時,這樣可以盡量避免空閑連接的切斷,但無法完全避免,因為無法預計連接會被空閑多久,如果你的系統不是總有人訪問的話,那么連接遲早會因為空閑而被切斷,導致一些不可預計的問題,而調大超時時間只是緩解而已

2、tcp keepalive功能#

tcp的keepalive,其實就是用來保持tcp連接的,其原理簡單說就是如果一個TCP連接在指定的時間內沒有任何活動,會發送一個探測包到連接的對端,檢測連接的對端是否仍然存在,如果對端一定時間內仍沒有對探測的響應,會再次發送探測包,發送幾次后,仍然沒有響應,就認為連接已經失效,關閉本地連接。
tcp keepalive並不是默認開啟的,在開發程序時可以設置tcp keepalive為true,這樣tcp連接在一定時間內沒有任何數據報文傳輸則啟動探測,這個時間一般是操作系統規定,Linux系統中可以通過設置net.ipv4.tcp_keepalive_time來修改,默認是7200秒,即2小時。當然在編程時也可以設置這個時間用於當前socket,但是Java的Socket API中好像只有設置keepalive=true,並沒法設置tcp_keepalive_time
當設置了tcp keepalive之后,只要tcp探測包發送的時間小於防火牆的連接超時時間,防火牆就會檢查到連接中仍然有數據傳輸,就不會斷開這個連接。

使用JDBC創建的數據庫tcp連接是沒有設置keepalive的,這點可以通過Linux的netstat或ss命令在數據庫客戶端(即應用端)驗證
使用命令netstat -ano 或 ss -ano,其中參數o都是顯示timer計時器,timer計時器在連接建立狀態下可以對連接保活計時
netstat命令對沒有開啟keepalive的tcp連接顯示為:off (0.00/0/0)
ss命令對沒有keepalive的tcp連接,不會顯示timer計時器

3、程序不定時執行查詢#

以上幾種方法要么是利用tcp連接keepalive特性,要么是采用數據庫端的空閑連接檢測,我們的程序中也可以主動做這種心跳檢測

Druid數據庫連接池從1.0.28開始,添加了druid.keepAlive屬性,默認關閉
打開druid.keepAlive之后,當連接池空閑時,池中的minIdle數量以內的連接,空閑時間超過minEvictableIdleTimeMillis,則會執行keepAlive操作,即執行druid.validationQuery指定的查詢SQL,一般為select * from dual,只要minEvictableIdleTimeMillis設置的小於防火牆切斷連接時間,就可以保證當連接空閑時自動做保活檢測,不會被防火牆切斷

 

這就是整個問題的解決,希望可以幫到大家。

 

https://blog.csdn.net/zqz_zqz/article/details/52235479


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM