前端很多時候是不會接觸到后端的工作,不過我們公司由于歷史原因,維護了大量的 Node.js 服務。
  所以也積累了一些后端優化的經驗,主要分兩塊 Node.js 和數據庫。
## 一、Node.js
  Node.js 的監控沒有從 0 開始,業務邏輯的日志直接記錄在阿里云中,性能監控部署的是阿里云提供的系統。
  業務邏輯包括自定義的埋點,以及數據庫的增刪改查操作,還有內部接口訪問的日志信息等。
  在性能監控中,可以導出內存快照,方便排查內存泄漏的問題。
**1)服務**
  Node.js 提供了定時任務、SSR、壓縮圖像、消息隊列等服務。
  定時任務可以將一些比較費時的計算按指定的間隔執行,以免直接訪問造成接口的慢響應。
  SSR 就是服務端渲染,是一種常見的頁面優化手段,[qwik](https://qwik.builder.io/)是一款語法接近 React 的前端 SSR 框架。
  其目標是延遲加載所有的代碼,例如一個按鈕在沒有點擊它之前,就不會去加載點擊的邏輯,甚至不會去加載 React 相關的代碼。
  理念很好,但是 qwik 還不夠穩定,應該會有比較多的坑。
  消息隊列是一種限流削峰的有效手段,一般由于瞬時訪問量過大,導致流量暴增,系統無法處理請求甚至發生崩潰。
  而在加入消息隊列后,系統可以從消息隊列中取數據,相當于消息隊列做了一次緩沖,極大地減少了業務處理的壓力。
**2)接口**
  在業務迭代和人員流動的過程中,就會出現一些冗余接口,而這些接口的訪問量還可能比較大。
  在對業務進行分析后,可以將其合并到另一個接口中,或者就是將接口響應直接寫到前端腳本中,直接減少一次通信。
  比較影響接口查詢時間的是與數據庫的交互,可以減少交互次數,例如增加一層緩存,無必要就不去查詢。
  或者要查詢多條記錄時,用一條 SQL 語句完成,可以在 SQL 使用 in 語法,例如 select \* from user where id in (1,2,3)。
  對于提交到服務器的數據,需要進行安全測試,即排除潛在隱患,防止刷接口,例如增加身份驗證。
  當代碼出現異常時(例如調的內部接口沒有響應),得避免接口奔潰,一直加載中,可以給到統一的響應。
  在統一響應的 JSON 格式后(例如 { code:0, data: {}, msg: "xxx"}),就可以比較方便的統計接口異常和規范前端代碼。
  規定 code 為 0 是正常響應,非 0 為異常后,就能統計各種異常響應的數量以及占比,進行針對性的優化。
  諸如監控、埋點等非業務且量比較大的接口,推薦單獨分離出來(可根據請求路徑進行服務轉發),以免出現錯誤時,影響線上業務,
**3)版本升級**
  公司之前所有的 Node 項目,其環境都是 8.9.4 版本,發布于 2018 年的一個比較古老的版本。
  老版本有兩個比較明顯的問題:
1. Node 高版本的特性和方法都無法使用。
2. 有些第三方新版本的包無法安裝和升級,該包可能依賴比較高的 Node 版本。
  之前在開發項目時就遇到第三方包自身的問題,必須升級或換個包才能解決,但因為 Node 版本的原因,無法替換,只能用其他方式來修補漏洞。
  2022 年的 7 月份,才有機會將版本升級到 16.15,總共有 4 個 Node 環境需要升級。
  在 4 個待升級的項目中,有 3 個是對外的項目,有 1 個是對內的項目。
  那么先升級對內項目的 Node 版本,這樣有兩個好處:
1. 即使出問題了,影響范圍也能最小。
2. 響應也能最及時,因為有問題的話,在公司內部能馬上反饋到我們組。
  我們每個項目都會有 3 套運行環境:測試、預發和生產。
  首先將測試環境升級,測試環境都是開發人員使用,影響最小,反饋最快。
  觀察一段時間后,再升級預發,預發環境與生產環境最為接近,數據庫采用的也是一套。
  最后才是生產,給真實用戶使用,再獲取反饋。從開始升級到全部項目升級完成,前前后后操作了 20 多天。
**4)框架**
  Node.js 的框架眾多,公司使用的是 KOA2,它非常輕量,諸如路由、模板等功能默認都不提供,需要自己引入相關的中間件。
  洋蔥模型的中間件非常強大,我們將異常響應、身份驗證、發送站內信等各類操作都交由中間件處理。
  各類框架都有其自身的特點和適用場景,選擇最合適自己團隊的框架才是正解。
  在公司的倉庫中,發現之前有用基于 next.js 建立的一個 SSR 項目,不過后面沒人維護,再之后就被運維釋放掉了。
  如果選擇了某一框架,那還是要盡量維護起來,投入到實際生產中,發揮其作用。
## 二、數據庫
  數據庫被分為關系型和非關系型數據庫,公司用的 MySQL 就屬于前者,而 Redis、MongoDB、ElasticSearch 等數據庫就屬于后者。
  線上數據庫也需要有個監控平臺運行著,了解數據庫的 CPU,定位慢查詢等。
**1)MySQL**
  在某張表的數據量上去后,性能瓶頸就會浮現出來。
  常見的優化包括創建索引、縮小查詢范圍等,利用執行計劃,可以觀察索引創建前后影響的行數。
  每次索引創建后,都能提高幾百、甚至幾萬倍的查詢速度,當然,創建索引也是有講究的,也不是隨便創建的。
  之前就遇到創建太長的索引,被拒絕了。
  另一種優化手段是將數據歸檔,就是將比較舊或不用的數據遷移至別處,另一個數據庫或直接下載到本地。
  當然,將數據存儲在云服務中,是會產生經濟成本的,所以還有種辦法就是直接刪除,這類通常是冗余數據,不會用到。
  對于非業務的數據表,也是像接口那樣需要分離,以免出現容量上限、慢查詢等問題而拖垮線上服務。
**2)MongoDB**
  自研的前端監控系統中的表字段有很多是 JSON 格式,這類數據其實很適合存儲在 MongoDB 中。
  因為 MySQL 需要先將對象進行字符串序列化,然后再存儲,而此時若需要以對象中的某個屬性作為查詢條件,MySQL 就難以實現了。
  不過,最終并沒有將數據存儲在 MongoDB 中,因為云服務只提供了 MySQL 的可視化操作界面和監控平臺。
  為了便于自己排查異常,還是選擇了 MySQL 作為存儲媒介,但公司有許多遺留業務都存儲在 MongoDB 中,日常還需要維護。
  所以后期自研了 MongoDB 可視化查詢工具,在后臺可以查詢線上的 MongoDB,省得每次都登錄服務器查看。
**3)ElasticSearch**
  當需要全文檢索一張千萬級的表時,在 MySQL 中實現就比較困難,很容易將數據庫掛起,并且在 5.7.6 之前的 MySQL 還不支持中文檢索。
  此時可用[ElasticSearch](http://www.hmoore.net/pwstrick/fe-questions/3210563)替代,這是一款基于 Lucene 的分布式、可擴展、RESTful 風格的全文檢索和數據分析引擎,擅長實時處理 PB 級別的數據。
  前端監控數據每日會增加 70W 條記錄,在 ES 中用關鍵字檢索這批數據會非常快。
  若數據中有比較大的字段,那么生成的倒排索引也會比較巨大,當時我存儲了接口的響應,結果整個容量直接膨脹了 3 倍。
  若要聯立好幾張表的 MySQL 數據,那么也比較適合存到 ES 中。
  例如用戶發布的日志,分成了 100 張表存儲在 MySQL 中,現在需要關鍵字檢索,就可以將這 100 張表合并到 ES 中。
**4)Redis**
  Redis 是一種支持 key-value 等多種數據結構的存儲系統,基于內存,可持久化,用于緩存、消息隊列、集群等場景。
  Redis 之所以快,一方面是因為大部分操作在內存中完成,以及采用高效的數據結構,例如哈希表、跳表等。
  另一方面,就是 Redis 采用了多路復用機制,使其在網絡 IO 操作中能并發處理大量的客戶端請求,實現高吞吐率。
  而 Redis 的并發處理能力(每秒處理請求數)能達到萬級別,甚至更高。
  前面也講到過,在接口中增加一層緩存,可加速接口響應,這也是緩存的常規用途,但是要注意,當緩存被穿透時,數據要能自動恢復。
  也就是說,緩存不保障數據的一致性,不能因為緩存的異常,而影響線上業務,將錯誤信息存儲到數據庫中。
  適當的將小部分特定的數據提前存入到 Redis 中,可起到預熱的作用,增加體驗。
*****
> 原文出處:
[博客園-前端體驗優化](https://www.cnblogs.com/strick/category/2360021.html)
[知乎專欄-前端性能精進](https://www.zhihu.com/column/c_1610941255021780992)
已建立一個微信前端交流群,如要進群,請先加微信號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