文章目錄
- Pre
- 1. 加索引:最低成本,最大收益
- 常見問題:
- 工具命令:
- 建議:
- 2. SQL 優化:比加索引再進階一步
- 常見 5 類問題:
- 實用建議:
- 3. 遠程調用:從串行改并行,性能翻倍不止
- 場景復現:
- 優化:并行調用
- 建議:
- 4. 數據異構:跨服務請求太慢?同步一份!
- 缺點:
- 5. 避免重復調用
- 案例 1:循環查數據庫
- 優化寫法
- 案例 2:死循環自旋
- 6. 異步處理
- 什么情況適合異步?
- 推薦工具:
- 注意:
- 7. 鎖粒度控制:避免全表級鎖
- 優化方式:
- 8. 分頁優化
- 大偏移問題:
- 推薦:
- 9. 加緩存:請求再多,讀緩存照樣穩
- 場景:
- 推薦策略:
- 注意:
- 10. 分庫分表
- 適用場景:
- 常見方案:
- 注意:
- 11. 輔助功能控制:接口不是萬金油
- 總結:性能優化是一門系統工程
Pre
性能優化 - 理論篇:常見指標及切入點
性能優化 - 理論篇:性能優化的七類技術手段
性能優化 - 理論篇:CPU、內存、I/O診斷手段
性能優化 - 工具篇:常用的性能測試工具
性能優化 - 工具篇:基準測試 JMH
性能優化 - 案例篇:緩沖區
性能優化 - 案例篇:緩存
性能優化 - 案例篇:數據一致性
性能優化 - 案例篇:池化對象_Commons Pool 2.0通用對象池框架
性能優化 - 案例篇:大對象的優化
性能優化 - 案例篇:使用設計模式優化性能
性能優化 - 案例篇:并行計算
性能優化 - 案例篇:多線程鎖的優化
性能優化 - 案例篇:CAS、樂觀鎖、分布式鎖和無鎖
性能優化 - 案例篇: 詳解 BIO NIO AIO
性能優化 - 案例篇: 19 條常見的 Java 代碼優化法則
性能優化 - 案例篇:JVM垃圾回收器
性能優化 - 案例篇:JIT
性能優化不是一蹴而就的,它是一次“認知 + 實踐”的雙向奔赴。
你是不是也遇到過這樣的場景:
- 某核心接口,TPS(每秒事務數)一上來就拉垮;
- 一個無害的查詢,突然把數據庫搞宕了;
- 接口明明邏輯簡單,但響應就是卡成 PPT。
接下來我將總結出的 11 條接口性能優化法則,其中不少優化點只需幾行代碼,就能把接口響應從 幾秒 → 毫秒。
1. 加索引:最低成本,最大收益
有沒有用索引?用了索引有沒有生效?MySQL 選對了索引嗎?
這三個問題必須要會回答。
常見問題:
WHERE
條件沒加索引;ORDER BY
字段無索引或順序不匹配;- 字段上存在函數、類型不一致導致索引失效;
- 多個索引沖突,MySQL 選錯了路徑。
工具命令:
SHOW INDEX FROM `table_name`;
EXPLAIN SELECT * FROM `table_name` WHERE cloumn= 1;
建議:
- 別忘了聯合索引的最左前綴原則;
- 必要時用
FORCE INDEX
; - 注意:添加、刪除索引都要評估業務負載,避免高峰期操作。
2. SQL 優化:比加索引再進階一步
索引優化完了,執行慢?那就看 SQL 本身。
常見 5 類問題:
- SELECT *(無腦全查)
- IN 太長或嵌套子查詢
- 關聯字段未加索引
- 過多 JOIN + 復雜嵌套
- 分頁偏移過大(OFFSET)
實用建議:
- 明確列名,不要
SELECT *
; - 拆子查詢,用臨時表換 IN;
- 使用
LIMIT
時避免大偏移(“延遲分頁”); - COUNT(*) 太大可用緩存兜底。
3. 遠程調用:從串行改并行,性能翻倍不止
場景復現:
某接口需要:
- 用戶服務:昵稱、頭像、等級
- 積分服務:積分
- 成長值服務:成長值
如果這樣寫:
A → 用戶服務(200ms)
A → 積分服務(150ms)
A → 成長值服務(180ms)
總耗時:530ms
優化:并行調用
CompletableFuture.allOf(userFuture, bonusFuture, growthFuture
).join();
三路并發,總耗時 = 最長的一次,約 200ms。
建議:
- 使用自定義線程池,避免高并發下線程爆炸;
- 注意業務合并時機和異常兜底。
4. 數據異構:跨服務請求太慢?同步一份!
高并發接口建議使用緩存異構數據,減少遠程依賴。
- 用戶信息 Redis 緩存結構:
{"userId": 123,"nickname": "Artisan","score": 87,"growth": 42
}
-
更新策略:
- 數據變更后同步更新 Redis;
- 可選:異步 MQ 通知,或雙寫機制。
缺點:
- 一致性需關注;
- 數據過期和失效策略要設計清晰。
5. 避免重復調用
案例 1:循環查數據庫
// 低效寫法
for (User u : list) {userMapper.getUserById(u.getId());
}
優化寫法
List<Long> ids = list.stream().map(User::getId).collect(toList());
userMapper.getUserByIds(ids);
減少 N 次 DB 請求 → 只要 1 次。
案例 2:死循環自旋
while (true) {if (flag) break;
}
- 慎用!在并發條件不明的場景下容易讓 CPU 打滿;
- 推薦用
LockSupport.park()
或CountDownLatch
等并發工具類。
6. 異步處理
什么情況適合異步?
- 寫操作完成后通知其他服務;
- 接口響應不需要等待的操作。
推薦工具:
@Async
CompletableFuture
- 消息隊列(RocketMQ/Kafka)
注意:
- 異步任務一定要兜底(失敗重試、冪等保障);
- 核心鏈路要保障同步結果,不建議異步。
7. 鎖粒度控制:避免全表級鎖
比如訂單取消邏輯:
synchronized(lock) {cancelOrder();
}
這種是“粗粒度鎖”。
優化方式:
- 基于訂單號加鎖(細粒度)
- Redis 分布式鎖(Redisson)
- 樂觀鎖:加版本號
version
8. 分頁優化
分頁是最常見的接口之一,但最容易踩坑。
大偏移問題:
SELECT * FROM order LIMIT 100000, 20;
掃描 10w 行,慢得要命!
推薦:
- 延遲分頁:記錄上次最大 id
- 游標分頁:基于上次的主鍵分頁
SELECT * FROM order WHERE id > lastId ORDER BY id LIMIT 20;
9. 加緩存:請求再多,讀緩存照樣穩
場景:
- 熱門xx詳情
- 用戶畫像、配置數據
- 訪問頻率高但更新低的數據
推薦策略:
- Redis String/Hash
- 結合 Guava Cache 做本地一級緩存
- 使用 CacheAside 模式:先查緩存 → 緩存失效再查庫
注意:
- 緩存過期 + 主動刷新;
- 防止緩存雪崩/穿透/擊穿(布隆過濾器 + 限流)。
10. 分庫分表
適用場景:
- 單表數據超千萬;
- 讀寫壓力過大。
常見方案:
- 垂直拆分:按業務分庫
- 水平拆分:按 ID 取模、按時間分表
- 中間件:ShardingSphere、MyCat
注意:
- JOIN 語句需要格外處理;
- 跨分片分頁/排序要小心。
11. 輔助功能控制:接口不是萬金油
很多接口包含了不必要的邏輯,尤其是在企業后臺管理系統中:
- 復雜日志打印;
- 權限、數據范圍過濾;
- 多租戶、國際化處理;
- 無用字段/參數傳遞。
建議:
- 精簡接口:核心功能優先返回;
- 將輔助功能放到獨立服務或中間件;
- 用 AOP + 過濾器統一處理非業務邏輯。
總結:性能優化是一門系統工程
接口優化,不是追求“代碼完美”,而是要用盡可能少的改動,達到可接受的性能表現。
提到的這 11 點優化手段,從數據庫 → 接口邏輯 → 緩存中間件 → 分布式架構全面覆蓋,幾乎涵蓋了主流系統中所有性能問題的“對癥下藥”。