我是怎么把一个“越改越慢”的接口,优化到稳定 100ms 的

5 阅读4分钟

我是怎么把一个“越改越慢”的接口,优化到稳定 100ms 的

在做业务开发时,我们很容易遇到一种情况:

一个接口最开始很快,但随着需求不断叠加,慢慢变得越来越慢,直到某一天被用户投诉。

最近我就接手了这样一个接口,从“能用”到“可用”,再到“可维护”,中间做了一次完整的优化,这里分享一下整个过程。


一、问题背景

这是一个典型的列表查询接口,功能是:

  • 查询用户的订单列表
  • 支持分页
  • 支持状态筛选
  • 返回订单 + 部分关联信息

初始情况:

  • 数据量:约 50 万
  • 平均耗时:800ms ~ 1.5s
  • 高峰期偶尔超过 3 秒

对于一个列表接口来说,这个性能显然不太能接受。


二、第一步:不要急着优化,先“拆解问题”

很多人一上来就开始:

  • 加缓存
  • 改SQL
  • 上索引

但如果没有搞清楚慢在哪里,很容易“优化了个寂寞”。

我先做了一件很基础但很关键的事情:

👉 把接口耗时拆开

大致拆成三块:

  1. 参数处理
  2. 数据库查询
  3. 数据组装(包括关联查询)

加日志统计后,结果很清晰:

  • 数据库查询:约 70%
  • 数据组装:约 25%
  • 其他:5%

👉 结论:主要瓶颈在数据库,其次是数据处理逻辑


三、第二步:SQL真的“简单”吗?

原始SQL大致是这样(简化版):

SELECT * FROM orders 
WHERE user_id = ? 
AND status = ? 
ORDER BY create_time DESC 
LIMIT ?, ?;

看起来没问题,但有几个隐患:

1. 使用了 SELECT *

这在数据字段较多时,会带来:

  • 不必要的IO开销
  • 网络传输浪费

👉 优化:只查必要字段


2. 分页越往后越慢

当分页 offset 很大时,比如:

LIMIT 10000, 10;

数据库需要:

  • 先扫描 10010 条
  • 再丢弃前 10000 条

👉 典型“深分页问题”


四、第三步:优化思路(分层处理)

这次优化我没有只做一件事,而是分三层处理。


✅ 优化一:建立正确的索引

原来只有单列索引:

(user_id)

但实际查询条件是:

  • user_id
  • status
  • create_time(排序)

👉 最终调整为联合索引:

(user_id, status, create_time)

效果:

  • 查询时间明显下降
  • 执行计划稳定

✅ 优化二:改造分页方式(关键优化)

把传统分页:

LIMIT offset, size

改为:

👉 基于游标(cursor)的分页

思路:

WHERE create_time < last_time
ORDER BY create_time DESC
LIMIT 10;

优点:

  • 不依赖 offset
  • 性能稳定
  • 越往后越快

这个改动对性能提升非常明显。


✅ 优化三:减少“隐形N+1查询”

原来的数据组装逻辑是:

  • 查订单列表
  • 循环每条订单,再查用户信息 / 商品信息

👉 典型N+1问题

优化方式:

  • 一次性批量查询关联数据
  • 使用 map 做内存关联

改完之后:

  • 数据处理时间直接下降一半

五、优化结果

最终效果:

  • 平均耗时:从 ~1s 降到 ~100ms
  • 接口稳定性明显提升
  • 高峰期无明显波动

六、这次优化最大的收获(不是技术点)

比起具体优化手段,我觉得更重要的是这几点:


1. 不要用“感觉”优化

很多时候我们会觉得:

  • “肯定是SQL慢”
  • “加个缓存就好了”

但实际上:

👉 数据才是唯一依据


2. 性能问题往往是“组合问题”

这次慢,不是一个点导致的,而是:

  • 索引不合理
  • 分页方式不对
  • 数据处理低效

👉 多个小问题叠加 = 大问题


3. 越早规范,成本越低

这些问题在数据量小的时候:

  • 完全看不出来

但一旦上量:

👉 全部暴露


七、给自己的一个复盘清单

现在我在写类似接口时,会默认检查:

  • 是否使用了联合索引?
  • 是否存在深分页问题?
  • 是否避免了 SELECT *?
  • 是否有N+1查询?
  • 是否可以减少不必要的数据?

这套检查下来,基本能规避 80% 的性能坑。


结语

很多性能问题,其实并不复杂,难的是:

在“看起来能用”的状态下,是否愿意去优化它

这次优化让我更清楚一件事:

👉 好的代码,不只是能跑,还要“跑得稳、跑得久”。