如何优化慢接口,慢SQL?

1 个回答

  • 武庚
    天越来越黑,星星会越来越亮。
    # 一、优化慢接口(端到端) **1) 先测清楚瓶颈在哪** * 接入 APM/RUM:分解耗时(DNS/连接/TLS/队列/应用/DB/外部依赖/缓存)。 * 采样慢调用堆栈(CPU/Wall-time flame graph),看是**CPU**、**I/O阻塞**、**锁**还是**下游**。 **2) 快速止血(常见 0→1 提升)** * **缓存**:本地/分布式缓存热点数据;幂等读多写少优先。 * **分页与惰性加载**:避免一次拉全;用\*\*关键字分页(keyset)\*\*代替 `OFFSET`。 * **合并与批量**:批量 RPC/批量 DB(如 `IN`/批量写、MQ 异步)。 * **连接复用**:HTTP keep-alive、连接池(Hikari/OKHttp)。 * **序列化优化**:避免深度反射;JSON 换成 fastjson2/jackson afterburner/ProtoBuf(按需)。 * **并发编排**:CPU 密集控线程,I/O 密集并发拉高;JDK21 可用**虚拟线程**做阻塞 I/O。 **3) 服务侧工程化** * **线程池**:有界队列 + `CallerRuns` 背压;避免“双重排队”。监控拒绝率/队列长度。 * **连接池**(HikariCP):`maximumPoolSize` 以 DB 能力为上限,别无脑放大;`leakDetectionThreshold` 辅助排查泄漏。 * **超时与重试**:区分**可重试/不可重试**,叠加指数退避;设置端到端超时预算。 * **读写分离/异步化**:强一致要求低的路径落 MQ/任务队列,接口只返回受理态。 # 二、优化慢 SQL(MySQL 为例) **1) 找到“真慢 SQL”** * 开启慢日志:`slow_query_log=ON; long_query_time=0.5`;用 `pt-query-digest` 聚合。 * 用 `EXPLAIN` / `EXPLAIN ANALYZE` 看执行计划:类型(`ALL`/`range`/`ref`/`eq_ref`/`const`)、扫描行 `rows`、是否 `Using filesort/temporary`。 **2) 索引与语句改写(高频套路)** * **覆盖索引**:`SELECT needed_cols` + 建**联合索引**覆盖 `WHERE` + `ORDER BY` + `LIMIT`。 * **联合索引顺序**:等值列在前,**范围列**在后;高选择性列靠前。 * **避免函数包裹索引列**:`WHERE DATE(create_time)=?` → `WHERE create_time >= ? AND < ?`。 * **避免前导通配**:`LIKE '%xx%'` 走不了索引;用倒排(ES)、`ngram`、`FULLTEXT` 或 `wildcard` 字段。 * **分页优化**:`ORDER BY create_time DESC LIMIT 50 OFFSET 500000` 改为**keyset**:`WHERE create_time < :last ORDER BY create_time DESC LIMIT 50`。 * **EXISTS 替代 IN/LEFT JOIN 去重**(大量数据时更稳): ```sql SELECT 1 FROM order o WHERE EXISTS (SELECT 1 FROM order_item i WHERE i.order_id=o.id AND i.sku=?); ``` * **聚合加速**:预聚合表/物化结果、分区表(按时间)、只扫热数据。 **3) 表与数据层面** * **热冷分离 / 归档**:把历史数据迁出主表,缩小工作集。 * **合理分区**:按时间范围查询明显时用 RANGE 分区;避免过多小分区。 * **外键/索引维护**:给外键列建索引,减少更新锁等待。 **4) 事务与锁** * 缩小事务范围;只锁必要行;减少长事务(binlog/回放压力)。 * 隔离级别:热点读写场景考虑 `READ COMMITTED` 降低 gap lock(按业务可行性)。 **5) 典型示例** * 需求:按 `store_no, emp_no` 查订单,按 `create_time` 倒序分页。 ```sql -- 索引 CREATE INDEX idx_order_store_emp_time ON t_order(store_no, emp_no, create_time DESC, id); -- 查询(keyset) SELECT id, no, member_name, create_time FROM t_order WHERE store_no=? AND emp_no=? AND create_time < ? ORDER BY create_time DESC, id DESC LIMIT 50; ``` 解释:联合索引覆盖过滤+排序+分页,扫描行数近似等于 `LIMIT`,避免回表和 filesort。 # 三、定位与验证的“固定流程” 1. **度量**:开慢日志/APM,确定热点接口 & 慢 SQL。 2. **归因**:看 DB CPU/IO、行锁等待、网络 RTT、对象分配/GC。 3. **方案**:索引/改写/缓存/批量/异步/拆表分库(按影响面由小到大)。 4. **压测**:用 JMeter/k6 + 真实数据分布,拉到 3× 峰值;监控 P95/P99、QPS、错误率。 5. **回滚预案**:特性开关、灰度、回退脚本。 # 四、面试回答模板(可直接说) > 我优化慢接口会先用 APM 拆分端到端耗时,判断是应用还是 DB。应用侧优先做缓存、分页、批量与并发编排,线程池/连接池用有界队列+背压。DB 侧先抓慢日志,`EXPLAIN/ANALYZE` 看是否全表扫、filesort、回表;通过**联合覆盖索引 + 语句改写(避免函数/前导通配)+ keyset 分页**把扫描行数压到 `LIMIT` 量级。最后用压测与监控验证 P95/P99,并留灰度与回滚。多数场景能把接口 P95 从几百 ms 降到百 ms 以内。
    发布于 2025-08-15 07:51 •IP属地 西藏
    举报
    评论
    收藏