最近閱讀了很多優秀的網站性能優化的文章,所以自己也想總結一些最近優化的手段和方法。
  個人感覺性能優化的核心是:減少延遲,加速展現。
  本文主要從產品設計、前端、后端和網絡四個方面來訴說優化過程。
## 一、產品設計
  在網上看到一句話:好的性能是設計出來的,而不是優化出來的。
  感覺好有道理,如果將性能瓶頸扼殺在搖籃里,那么后期的維護成本將變得非常低,并且在避免改造后,都不需要浪費資源了。
**1)聊天改版**
  最近公司有個聊天活動做改版,改版上線后驚喜的發現,[監控數據](https://www.cnblogs.com/strick/p/14577054.html)居然減少了將近20W。
:-: 
  更深一步的探究,才知道在頁面初始化時,會發一個請求,去獲取一些文案,但其實這些文案都是寫死的,完全可以寫到頁面本地。
  如果在產品設計時,就不是通過接口讀取的方式,那么就可以大大減少瀏覽器和服務器之間的通信。
**2)數據遷移**
  公司有一個審核系統,最近審核人員反應系統使用起來越來越卡。排查后發現,定位到性能瓶頸,是因為數據表巨大導致的,目前已有6千萬條。
  其實在一開始設計時,就該能預估到,未來的數據量,感覺還是偷懶了,想快速完成。感嘆前人挖坑,后人埋坑。
  起初是想分表,但思慮后還是決定不那么做,最后采用的是[MySQL歸檔](https://www.cnblogs.com/strick/p/15412413.html)。
  前前后后花了兩周的時間,才將方案落地實現,涉及到運維、QA和我們前端組,人力成本無疑是巨大的。
  其實在每次完成優化后,可以再想想能不能將問題泛化,擴大維度,想想其他地方是否有類似問題,總結經驗供他人學習,以及對未發生的問題進行預判。
**3)大批量的504**
  這是在上線監控平臺后不久發現的,504 的請求量太多,這類通信錯誤居然占了25%~30% 左右。
  經過排查定位到一個常規的翻卡活動中,產品設計出現了嚴重的問題。
  在進行一個翻卡操作時會請求兩個接口,并且每個接口各自發送 3 次通信,這樣會很容易發生 504 錯誤,每天大約有1500個這樣的請求。
  首先是給其中一張表加了個索引,然后是將兩個接口合并成一個,并且每次返回 20 條以上的數據,這樣就不用頻繁的發起請求了。
  經過改造后,每日的 504 請求從 1500 個左右降低到 200 個左右,減少了整整 7.5 倍,效果立竿見影,大部分的 504 集中在 22 點到 0 點之間,這段時間的活躍度比較高。
  還有一個令人意外的發現,那就是監控日志的量每天也減少了 50W 條。
## 二、前端
  前端的優化手段非常多,常規的其實就是延遲請求和減少頁面渲染次數。
**1)預請求**
  數據預請求是將數據預請求的時機由業務發起請求的時機,提前到用戶點擊時,并行發送數據請求,縮短數據等待時間。
  這個需要客戶端配合,在Webview啟動的時候,并行的去請求首屏需要的數據。
  經過觀察發現每次請求后端數據的API大概要花100~200ms,如果把這段時間省下來,那么也能減少白屏時間。
  在與客戶端溝通時,他們還是表現的比較抗拒的,在他們看來這點時間微不足道,還會增加他們的工作量。
  有個人還一直強調瀏覽器內置的緩存規則是一劑靈藥,絕對可以提升頁面的性能,經過討價還價后敲定最終的[預請求方案](https://www.cnblogs.com/strick/p/14918217.html)。
**2)預加載和懶加載**
  預加載和懶加載特指的是圖片的加載,其實就是延遲請求時機。
  兩者的核心算法就是計算圖片的相對位置,具體[參考于此](https://www.cnblogs.com/strick/p/5372694.html)。
  這兩招非常有效,可以說是立竿見影,尤其是頁面中圖片量較多以及尺寸較大時,能瞬間提升頁面加載。
  之前做過一次偏動畫的宣傳頁,在進入頁面時,立刻請求所有圖片,直接白屏好幾十秒。
**3)串行變并行**
  有一張活動頁面,交互并不多,但是要展示的數據需要經過后臺比較復雜的計算后才能得到。
  一開始是將所有的數據都包含在一個接口中,這就導致頁面發起請求后,要白屏好幾秒后,才能慢慢加載。
  用戶的等待時間很長,經過排查后發現,其實可以將一些配置參數提取到單獨的一個接口中,核心的數據再抽取到另一個接口中。
  這樣就是將一個串行的接口分離成兩個并行的接口,先快速的讓用戶看到整個頁面的大結構,例如頭圖、規則等信息。
  然后在將數據渲染到合適的位置,形成了一個時間差,對用戶更加友好。
## 三、后端
  后端主要涉及是Node.js和數據庫的優化,我們組涉及到的此類性能瓶頸并不多,但也會有一些。
**1)內存泄露**
  自研了監控平臺,在剛上線時,就遇到了性能問題,首先是采集的參數在大批量的發送給存儲的服務器時,服務器宕機了。
  因為每秒就有幾百甚至幾千的請求發送過來,服務器需要整理數據后加入到數據庫中,當數據量暴增時,服務器就會來不及處理。
  這個性能瓶頸發生后,馬上加入任務隊列,服務器就能平穩的運行了。
  之后又出現了另一個問題,CPU會在不特定時間段(例如21~22、23~02等)飆到70%,內存也是一路飆升不會下降,明顯是出現了內存泄漏,具體的排查過程可以[參考于此](https://www.cnblogs.com/strick/p/14754867.html)。
  開通了阿里云的 Node.js 性能平臺,在前前后后調試了三周,有點像探案的過程,才最終定位到一段最為可疑的代碼。
  原來是為外部的一個對象反復注冊了一個事件,應該是形成了一個閉包,讓閉包內的變量都無法釋放。
**2)數據庫**
  仍然是在研發監控平時時遇到的性能瓶頸,這次主要體現在數據庫。
  監控的日志我都存儲在MySQL中,每日 70W 條記錄,數據量很快達到了千萬級別,在做查詢時,變慢了很多。
  那么就得讓索引上了,亞洲舞王尼庫拉斯趙四曾說過:一個索引解決不了的,那就來兩個索引。
  我也是這么做的,加了好多個索引,但唯獨模糊查詢,這個不是加索引能解決的,嘗試了很久,最終還是決定遷移到ElasticSearch中。
  效果也是立竿見影的,幾千萬條數據,幾秒鐘就出了結果,令我驚嘆不已。
  這里順便說下,我會在什么場景中選擇 MongoDB 作為數據庫的。
  MySQL是一種關系型數據庫,會事先聲明表結構,那么對于數據結構不定的記錄,存儲起來會比較費勁。
  例如記錄中有許多JSON格式的數據,就比較適合存在 MongoDB,當然了,這類數據也可以轉換成字符串存在 MySQL 的一個字段中。
  只是在讀取和寫入時,要經過序列化和反序列化的操作,并且如果要查詢JSON數據的某一個字段時,MySQL中就只能模糊查詢了。
  但是平時的話,我還是會盡量將數據存在 MySQL 中,有個很直接的原因是因為線上的 MySQL 有一個功能完善的可視化界面,而 MongoDB 沒有,只有一個我自制的簡陋工具。
**3)管理后臺掛起**
  之前也會偶爾有管理后臺服務掛起的情況發生,但最近會在每天的10點左右發生。
  在下圖中,一分鐘內server服務瞬間飆到了 100%,不得不讓我引起注意。 ?
:-: 
  查找那段時間的日志,發現有很多送禮的記錄,反向查詢后,定位到一個導出鏈接。
  初步以為是這個功能導致的服務掛起,詢問相關操作人員,得到當時的操作過程。
  在預發環境模擬,得到的結果是可以正常導出,所以應該可以排除它的嫌疑。
  回想到前天也發生了掛起(不過前天的CPU都比較正常),再次翻查日志,新發現直指審核模塊。
  與審核人員溝通后,她們向我一頓吐槽,大倒苦水,說這系統11點后,經常卡頓轉圈。
  這個系統后續有很大的優化空間,那么目前首要任務是解決服務掛起的問題。
  可以想到的是將審核模塊單獨配置一臺服務器,做成獨立服務,這樣也便于觀察。
  服務切換其實就是將特定規則的API指向另一個服務,代碼和邏輯都不需要改動,非常方面。
  隔天發現仍然會掛起,但是掛起的服務可以定位到審核模塊,運維也找到了一條發生504的接口。
  前一天發生的掛起,也是這個接口引起的,順著這條接口查看代碼,并沒有發現什么異常。
  打開日志,發現這個接口的響應的確非常慢,要幾分鐘了,再去查其中涉及到的SQL查詢語句,發現也很耗時。
  將語句拿到數據庫中執行 EXPLAIN 命令,影響的行數要 311W 多條,命中的索引是 idx\_create\_time。
  而去掉其中的一個日期限制后馬上降到 69 條,命中的是一個聯合索引,回到代碼中,查看邏輯后,發現這個時間限制是多余的,馬上就將其去除。
  還有一條SQL語句運行也很慢,影響的行數要 16W 多條,命中的索引是 idx\_status,而這個 status 其實只有幾個可選的關鍵字,會影響查詢效率。
  于是再創建一個聯合索引,影響的行數降到 1785 條,優化后,再反饋給審核人員。
*****
> 原文出處:
[博客園-Web優化躬行記](https://www.cnblogs.com/strick/category/1795726.html)
[知乎專欄-Web優化躬行記](https://zhuanlan.zhihu.com/c_1260996761008627712)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎閱讀。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020