【性能优化】一个接口从 5 秒到 0.1 秒优化过程

2026-10-08 00:00:00

起因

ZhaoTab 项目里一个简单的查询接口耗时 5 秒,这显然不太正常。研究一番后,我把它提速到了 0.1 秒。本文记录整个优化过程。

在浏览器控制台的 Timing 选项卡中,可以看到耗时分布如下。

阶段 耗时
Waiting for server response(TTFB) 3.07s
Content download 2.27s
响应大小 290KB

这 5 秒由两段时间叠加而成,两者的成因完全不同,需要分开来看:

下载慢

运营商所说的千兆带宽 1000Mbps,指的是每秒能传输 兆比特 的数据。1 字节 = 8 比特,换算成容量单位要除以 8,所以 1000Mbps 意味着理论峰值每秒能传输 125MB 的数据。

我的服务器带宽只有 1Mbps,理论峰值每秒只能传输 125KB 的数据。所谓理论峰值,是指同一时刻只有一个人在从服务器下载数据;如果有两个人,每秒能传输的数据就要对半分,只有 62.5KB。

数据只有 290KB,并不算大,但下载花了 2.27s,这与服务器 1Mbps 的下载速度是相符的。

# 查看响应头里是否有 Content-Encoding
curl -s -o /dev/null -D - -H "Accept-Encoding: gzip" \
  https://api.example.com/api/v1/store-apps | grep -i content-encoding

发现响应头里没有 Content-Encoding,这意味着 API 数据没有经过压缩。

于是在 nginx 上给 API 开启 gzip:

gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_proxied any;   # 反向代理过来的响应也要压缩
gzip_vary on;
gzip_types application/json text/plain application/javascript text/css;

同时对代码也做一下优化,把一些数据库中存在、但前端并未用到的字段直接排除掉。

经过上面两个步骤,响应数据从 290KB 降到了 48KB,Content download 从 2.27s 降到 47ms。

如果财力允许,可以增加带宽,那下载速度回更快,目前 47ms,不到 0.1s (100ms) 完全能接受。

服务器响应数据慢

服务器响应速度慢,其实有以下几个原因:

  1. 数据库查询慢:没有索引导致全表扫描,或者排序没用上索引。这是最常见的猜测,也是最初想到的方案——「加索引」。
  2. 后端处理慢:序列化、拼装数据耗 CPU;或者异步服务的事件循环被同步代码阻塞。
  3. 服务器负载高:CPU、内存被其他进程占满。
  4. 后端和数据库之间的网络慢:查询本身很快,但结果从数据库传到后端的这一段很慢。

排查原因 1:用 explain 看数据库自己花了多少时间。

plan = col.find({}).sort("order", 1).explain()
stats = plan["executionStats"]
print(stats["executionTimeMillis"], stats["totalDocsExamined"])

结果:

475 条数据、总共约 320KB,全表扫描再排序,MongoDB 自己连 1 毫秒都用不到。原因 1 排除了——加索引能省下的时间不到 1ms,跟 3 秒相比可以忽略不计。

排查原因 2:这就是一个简单的查询接口,没有额外操作,排除。

排查原因 3:登录服务器,用 top 命令查看,CPU 和内存都正常,排除。

排查原因 4:在本机写一个脚本,直连同一个数据库执行同样的查询,测量把全部结果取回来要多久:

t = time.time()
docs = list(col.find({}).sort("order", 1))
print(len(docs), time.time() - t)
查询 结果
475 条(约 320KB) 3.06s
90 条 0.5~0.8s

数据库执行 0ms,但把结果取回却要 3 秒,耗时和数据量成正比。时间几乎全部花在了数据从 MongoDB 传到客户端的过程中,速度大约 1Mbps,正好和第一轮测出的服务器公网带宽一致。

本机走公网连数据库慢,这很正常。问题是:线上后端和数据库在同一台服务器上,为什么也要 3 秒?

最后经过排查发现:虽然数据库和后端在同一台服务器上,但后端代码里 MONGODB_URL 的默认值,以及服务器上的实际配置,连接串里写的都是数据库的公网域名,而不是 127.0.0.1。

这样一来,后端去连同一台机器上的 MongoDB,流量会先出到公网再绕回来,和在本机连服务器上的数据库耗时是一样的。

把服务器上后端的连接串改成本机回环地址,然后重启后端:

# 改前(示意)
MONGODB_URL=mongodb://<user>:<password>@db.example.com:<port>/<db>
# 改后
MONGODB_URL=mongodb://<user>:<password>@127.0.0.1:<port>/<db>

再次测试时,Waiting for server response 直接从 3.34s 降到了 69ms。

最终效果

阶段 优化前 优化后
Waiting for server response 3.07~3.34s 69ms
Content download 2.27s 47ms
传输体积 290KB 约 48KB
合计 约 5.3s 约 0.12s

改动加起来就这些:nginx 加了几行 gzip 配置,接口少返回几个字段,服务器上改了一行数据库连接串。没有加索引,也没有升级服务器带宽。

↶ 返回首页 ↶

本文总阅读量  次
皖ICP备17026209号-3
总访问量: 
总访客量: