程式碼優化
改善使用者可見的效能與可靠性,同時保持程式碼的可理解性與營運成本的比例合理性。針對現有的工作負載進行優化;不要為了假設性的未來規模而增加複雜度。
工作流程
- 定義目標與護欄。 找出相關的端點、任務、查詢、工作負載與期望的結果。僅選擇重要的指標:例如,p95/p99延遲、吞吐量、CPU、記憶體、分配速率、資料庫查詢次數、依賴往返次數、錯誤率或飽和度。保留行為、一致性、安全性與現有的錯誤合約。僅在缺少目標或工作負載導致無法做出正確選擇時,提出一個聚焦的問題;否則,根據請求和程式碼推斷你能得出的資訊。
- 建立基線。 使用現有的追蹤、剖析、查詢計劃、指標、測試或具代表性的基準。保持前後的輸入、環境與並發度具有可比性。不要憑直覺或單一噪聲計時來聲稱速度提升。避免未經授權就對生產環境執行負載測試或昂貴的查詢分析。
- 找出瓶頸。 追蹤端到端的關鍵路徑,並使用剖析或儀器來區分CPU工作、阻塞、資料庫時間、網路等待、序列化、鎖競爭與排隊。檢查實際的查詢/呼叫模式與具代表性的資料。優先選擇證據,而非廣泛的清理或微優化。如果瓶頸尚未確定,請報告不確定性,並在進行侵入性變更前提出最小的有用測量。
- 選擇最小有效變更。 在可行時,一次優化一個具體的瓶頸。保留輸出、順序、失敗行為、交易語意與安全性檢查。不要為了基準測試的勝利而犧牲正確性或可靠性。遵循儲存庫的慣例,並避免不相關的重構。
- 驗證兩者結果。 執行相關的正確性測試,並比較前後相同的具代表性工作負載。檢查目標指標是否改善,以及護欄——特別是尾端延遲、錯誤、記憶體與依賴飽和度——是否沒有退化。如果無法進行有意義的比較,請說明,並避免將未經測量的變更呈現為已驗證的優化。
- 報告結果。 簡要說明瓶頸與證據、所做的或建議的變更、驗證與測量到的影響(或為何無法測量),以及任何重大的取捨。對於審計或計劃請求,請根據證據、可能影響、工作量與風險來優先排序發現,而不是更改程式碼。
資料庫與資料存取優化
- 計算查詢次數並檢查ORM/資料庫存取模式。使用適當的聯結、批次或基於集合的操作來消除N+1讀取或寫入;驗證查詢次數並保留結果順序與缺失記錄的行為。
- 僅擷取需要的欄位與列。對大型結果集使用分頁或串流;將合適的過濾與聚合推向資料庫,而不是將大型資料集載入應用程式記憶體。
- 使用查詢計劃與真實謂詞來證明索引的正當性。檢查選擇性、排序/聯結需求、現有索引與寫入/儲存開銷。不要新增推測性或重複的索引。在具代表性的資料上驗證計劃與延遲;僅在安全的環境中使用基於執行的計劃分析。
- 減少不必要的往返,並將交易與鎖的範圍保持在所需的最小值。不要為了捷徑而削弱約束、隔離、驗證或一致性。
- 僅在測量到的重複使用證明其正當性時,才新增快取。定義新鮮度、失效、鍵範圍與失敗行為;不要將快取作為對緩慢查詢的預設回應。
後端與服務呼叫優化
- 移除冗餘呼叫,批次處理相容的請求,並且僅對真正獨立的呼叫進行並行化。保持並發度受限,保留所需的順序/依賴關係,並考量下游容量。
- 重用客戶端與連線池。設定符合請求預算的截止時間/逾時,並在支援的地方傳播取消。
- 僅對短暫故障進行重試,且操作必須是安全可重試的。限制嘗試次數與總重試時間,使用帶抖動的退避策略,並避免在多個層級進行重試而放大負載。不要在沒有示範失敗模式的情況下新增重試、斷路器或佇列。
- 僅在有效負載過大或重複序列化對測量路徑有重大影響時,才進行縮減。保留所需的服務失敗語意;對於可選依賴,明確說明優雅降級。
執行階段、可靠性與可維護性
- 優先選擇有證據支持的最高槓桿修復:改善演算法複雜度、移除重複工作、減少不必要的分配/複製,或在進行低階微優化之前先解決阻塞與競爭問題。
- 保持資源使用在並發下受控:考量記憶體、佇列、池、扇出、逾時、取消與背壓。確保失敗不會造成重試風暴、無限制的工作或資源洩漏。
- 將效能與可靠性視為系統層級的結果。一個較快的呼叫者若超載資料庫或增加錯誤率,就不是優化。
- 應用YAGNI原則:不要為了假設性的瓶頸而新增依賴、抽象、快取、索引、非同步層、配置選項或架構變更。優先選擇清晰的局部變更;如果需要複雜度,請解釋測量到的效益及其維護成本。
- 不要為了節省微不足道的時間而移除輸入驗證、授權、可觀測性或安全檢查。不要將可讀的程式碼替換為更難維護的微優化,除非測量到的增益是實質的。
驗證注意事項
- 比較等效的工作負載,使用可重複的執行與足夠的樣本,以區分訊號與雜訊。在相關時使用分佈(尤其是尾端延遲),而不僅是平均值。
- 驗證受批次、快取、並行、分頁、重試或並發影響的功能行為與邊界案例。新增聚焦的測試,以保護有意義的不變量,例如查詢次數、順序、逾時行為或可選服務的備援。
- 變更後重新剖析:瓶頸會移動,局部改善可能將成本轉移到另一個服務或資源。
- 如果優化沒有產生實質改善,或其複雜度超過效益,請還原它或建議不要這樣做。