# 前後端效能優化工具 + 範例 + 心得 ## 前端優化範例 某 school 反應 `admin/students/edit` 很慢 每次進入該頁面 progress bar 需要跑個 20 秒 ## 前端優化範例 - 工具 - chrome or edge ![](https://i.imgur.com/lO3mKHT.png) ## 前端優化範例 - 工具 - chrome or edge ![](https://i.imgur.com/D8zUWHb.png) ## 前端優化範例 - 工具 - chrome or edge ![](https://i.imgur.com/OEC5ULm.png) ## 前端優化範例 - 如何抓到問題點 1. 先找耗時最久的區塊(火焰圖上最大的區塊) 2. 找看看 1. 有沒有重複的區塊一直出現(重複的呼叫) 2. 有沒有某些區塊特別大的(執行速度很慢的 function) ## 前端優化範例 - 解決方式 此範例的問題點: - 當 conditional field 的值有變化時,關聯的 field 會需要根據值改成「顯示」或「隱藏」。 - 這邊會把所有的 conditional fields 都檢查一次,尋找關聯的 field 時會使用 jQuery 的 find - 問題點:同樣的 field 會被重複使用 `find` 拿 解決方式: - 先把 field cache 起來,避免重複呼叫 `find` 取得 field ## 前端優化範例 - 結果 Before: 358.31ms After: 36.70 ms ![](https://i.imgur.com/VGZvpII.png) ref: https://github.com/eduvo/openapply/pull/14478 ## 後端效能優化 - 常見問題 - N + 1 - DB 沒有加上適合的 index - DB Transaction 太久 - 一次從 DB 拿太多東西出來 - 從 DB 拿了不需要的東西出來 ## 後端效能優化 - 什麼時候需要優化? - 不破壞程式可讀性,且可以簡易優化的地方,隨時可以優化 - N + 1 query - DB 沒有加上適合的 index - 系統瓶頸 - 客戶反應有問題的地方 ## 後端效能優化 - 檢查工具 newrelic ![](https://i.imgur.com/pumK6N7.png) ## 後端效能優化 - 檢查工具 gem - bullet https://github.com/flyerhzm/bullet ![](https://i.imgur.com/ReiwdmO.png) 不過也要注意在加上 includes 時是否要需要加上限制條件,避免拿太多 objects 出來 ## 後端效能優化 - 檢查工具 explain ![](https://i.imgur.com/yxVEJPZ.png) ## 後端效能優化 - 檢查工具 gem ruby-prof + gem ruby-prof-flamegraph + flamegraph generator https://ruby-prof.github.io/ https://github.com/brendangregg/FlameGraph ![](https://i.imgur.com/LuCCcgm.png) ## 後端效能優化 - 優化 N + 1 query 可以使用 includes 修正 ![](https://i.imgur.com/RdwJr1w.png) 不過也要注意在加上 includes 時是否要需要加上限制條件,避免拿太多 objects 出來 ## 後端效能優化 - 優化 bulk insert 或 bulk update gem - activerecord-import https://github.com/zdennis/activerecord-import 可以加速 bulk insert & bulk update 並支援 MySQL 的 ON DUPLICATE KEY UPDATE ## 後端效能優化 - 優化 query 太慢 加 DB Index explain ![](https://i.imgur.com/rTgnXLn.png) 加上 status index 後 ![](https://i.imgur.com/vk6QHvu.png) | | before | After | | ---- | ------ | ------------------------------ | | Type | ALL | Ref | | Key | NULL | Index_pamoja_courses_on_status | | Rows | 169 | 104 | 部分 query 會需要多個欄位合併搜尋,可以考慮加 multiple-column Indexe 譬如 checklist_items 的 idx_checklist_items_unique_school_checklist_item_id_student_id (school_checklist_item_id,student_id,active) UNIQUE ## 後端效能優化 - 優化 query 太慢 可能是拿太多不需要的 row,可以用 where 或是 join 加上限制 Example 1: 譬如我需要的是 enrolled 的 pamoja_courses,那 query 就要下 `PamojaCourse.where(status: 'enrolled')` 而不是 `PamojaCourse.all` Example 2: 譬如我需要的是「有 parent」 的 student 那就可以用 `Student.joins(:parent)` 來限制 ## 後端效能優化 - 優化 query 太慢 可能是一次拿太多資料 可以利用 find_each 或 find_in_batches 分批拿 https://api.rubyonrails.org/classes/ActiveRecord/Batches.html#method-i-find_each ## 後端效能優化 - 優化 query 太慢 如果某個計算比較慢,但是參數不常變化 可以考慮放到 Rails 的 Cache 裡面 ## 後端效能優化 - 優化使用者體驗 如果某段程式裡面含有執行速度較慢的程式,但不需立即得到結果 可以考慮把該段程式放到 background job 裡面 ## 後端效能優化 - 程式執行較慢 有一種情況是拿了不需要的資料,可以利用 select 或 pluck 拿需要的欄位即可 譬如 ![](https://i.imgur.com/DSk9wtF.png) ## 後端效能優化 - 程式執行較慢 其他 ruby 程式的優化可以參考 https://github.com/JuanitoFatas/fast-ruby ## 後端效能優化 - 驗證工具 benchmark-ips https://github.com/evanphx/benchmark-ips ```ruby= ActiveRecord::Base.logger = nil Benchmark.ips do |x| x.report('without select') do Student.all.map{|s| s.name } end x.report('with select') do Student.all.select(:first_name, :last_name, :preferred_name, :other_name).map{|s| s.name } end nil end ``` ![](https://i.imgur.com/DSk9wtF.png) ## 後端效能優化 - 驗證工具 scientist https://github.com/github/scientist https://github.com/eduvo/openapply/pull/14760/files#diff-02ece8ad5786f65765ddb7e18dfba6a384fff6b9882ab77af6cd3927524ba191R20 ```ruby class ScientistExperiment include Scientist::Experiment # ... def publish(result) puts "=================================================" puts "matced? #{result.matched?}" puts "science.#{name}.control: #{result.control.duration}" puts "science.#{name}.candidate: #{result.candidates.first.duration}" puts "=================================================" end end ``` ``` ================================================= matced? true science.family-roster-collect-ids.control: 4.199053999999705 science.family-roster-collect-ids.candidate: 0.04198200000064389 ================================================= ```