Skip to main content
Version: 1.0

高并发接口优化

「接口访问过多,导致响应慢该怎么做?」——面试官最看重思路的分层,不是背一串名词。

第一步:先定位,不要瞎猜​

  1. 看监控:QPS、RT 曲线,确定是单个接口慢还是整体慢;
  2. 链路追踪(SkyWalking)找最慢环节:DB?外部调用?锁竞争?
  3. 慢 SQL 日志 + 线程 dump,拿到证据再动手。

缓存:让大部分请求不打到数据库(优先级最高)​

请求链路:浏览器缓存 → CDN → 网关缓存 → 应用本地缓存(Caffeine) → Redis → DB
  • 读多写少的数据(商品详情、配置、热搜榜)放 Redis,TTL + 随机退避防雪崩;
  • 热点 key 用本地缓存兜一层(注意一致性:写后失效);
  • 缓存命中率 ≥ 90% 时,数据库压力直接降一个量级。

限流:超过容量的流量直接拒绝 / 排队​

层级手段
Nginxlimit_req_zone 按 IP / URI 限速
网关层Sentinel / Spring Cloud Gateway + Redis:接口级 QPS 限流
业务层注解式限流 @RateLimit(key = "'login:' + #ip", qps = 5")(Redis + Lua 令牌桶)
防刷验证码、同一账号登录失败 5 次锁定 10 分钟

Redis + Lua 令牌桶核心思路(原子执行,避免超发):

// KEYS[1]=限流key, ARGV: 容量/速率/当前时间 → 返回 1 放行 / 0 拒绝
Long allow = redisTemplate.execute(RATE_LIMIT_SCRIPT, keys, capacity, rate, now);
if (allow == 0) {
throw new BizException(42900, "请求过于频繁,请稍后再试");
}

异步 & 削峰:请求处理路径越短越快​

  • 非核心逻辑异步化:发通知、记日志、埋点 → @Async 线程池或 MQ,接口耗时只保留主链路;
  • 写请求削峰:秒杀 / 下单类场景请求先进 MQ 排队,消费端按数据库能承受的速率处理,用户端轮询或推送结果;
  • 并行调用:接口内多个无依赖的下游调用用 CompletableFuture.allOf 并行,总耗时 = 最慢者。

数据库层:慢 SQL 是接口慢的常见根因​

  • 慢查询日志 + EXPLAIN 逐条治理(索引、深分页、大事务);
  • 连接池调优(HikariCP:maximumPoolSize 不要盲目调大);
  • 读写分离分摊读压力。

扩容与兜底​

  • 水平扩容:服务无状态化 → 加机器 + 负载均衡,容量线性提升;
  • 熔断降级:依赖的下游故障时快速失败(Sentinel / Resilience4j),非核心功能降级返回默认值,防止故障雪崩;
  • 事前:压测(JMeter / wrk)摸清容量水位,提前扩容 + 预热缓存;
  • 事后:监控告警(Prometheus + Grafana),加链路追踪留证据。

:::tip 一句话收尾(面试用) 单接口慢先看慢 SQL 和外部调用;整体慢优先加缓存;流量超容量用限流保护自己;写入洪峰用 MQ 异步削峰;还扛不住就无状态扩容;极端情况降级保主链路。核心思想:每一次流量增长,都别让数据库独自硬扛。 :::