並發500,2000次請求,從10s到300ms的接口優化,我做了哪些事


  小程序的接口剛寫完時,一個接口的調用時間大概是700ms左右,100並發的100請求的壓測結果是9s,用的都是項目的底層方法(有緩存),框架是ci.而上線城市服務的三星基准是500並發500ms以下,遠遠不達標,於是乎開始了漫長的優化.在此記錄一下從10+s優化到300ms的過程.

  • xhprof分析了代碼,發現ci的cache的redis驅動做了這樣一件事,耗費了大量的性能
    • $serialized = $this->_redis->sMembers('_ci_redis_serialized');
              if ( ! empty($serialized))
              {
                  $this->_serialized = array_flip($serialized);
              }

      ci的redis驅動初始化程序里有這樣一段代碼,這段代碼的含義是將"_ci_redis_serialized"這個集合中的所有成員都取出來.然后反轉鍵值對.而_ci_redis_serialized這個集合對於ci的涵義就是為了記錄序列化后的數據.因為ci的cache可以把任何數據都放入cache中,包括字符串,數組,對象等,而redis只能存儲一種數據格式.所以就需要將數組或者對象序列化存入redis中(當然如果你不打算存儲對象完全可以用json格式來存儲數據).問題就出在這里,一個項目的key往往是數量級的增長的,像我們就有16w的key是經過序列化存放在這里的(除了數據緩存,還有大量的用戶session數據也是需要序列化的),在大量的cache操作下,這個過程就耗費了大量的性能.解決方案是:既然用了集合,直接用SISMEMBER來判斷這個集合中是否有這個成員就行了,redis的內置方法性能是非常好的(好到可以忽略)這次優化后從10+s下降到了3s左右.

  • 在獲取數據的底層方法上,在小程序再封裝一層
  • 只提供給前端必要的字段
    • 這兩個步驟讓時間下降到了1s+,很簡單的邏輯,減少不必要的數據傳遞,減少冗余數據.
  • 去除session中的_get_lock,這個是防止兩個進程同時寫一個key,因為基本發生不了所以先去除
  • 不經過service層,直接訪問數據庫,為了能讓service層(數據層)能單獨部署,所以內部訪問模型是通過curl來請求的.現在改造成直接通過ci的model來訪問(也就是直連數據庫了)
    • 這兩個步驟讓時間下降到了600~700ms,基本上沒有太大的優化.
  • 最后的優化是OPcache,讓服務器來緩存php文件.
    • 最后這個步驟讓時間優化到了300ms,單獨訪問一次接口只要30ms左右.弊端是當你發布文件后,可能最多會有1分鍾的生效期(類似於緩存了1分鍾的本地文件)

 

  最后的結果就是小程序的壓測結果通過啦~


免責聲明!

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



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