如何设计一个高并发接口?

4 阅读9分钟

一、先明确高并发接口到底要解决什么问题

高并发接口的核心目标通常有 4 个:

  1. 扛得住流量
  2. 响应足够快
  3. 数据不能乱
  4. 高峰期系统不能崩

比如电商场景里:

  • 秒杀下单接口
  • 抢优惠券接口
  • 大促查询商品库存接口
  • 热门活动报名接口

这些都属于典型高并发接口。

所以我设计时会先问自己一句话:

这个接口是读多写少,还是写多读少?
因为两种设计思路完全不一样。

  • 读多写少:重点是缓存、CDN、热点隔离
  • 写多读少:重点是限流、削峰、异步、幂等、一致性

二、设计高并发接口的整体思路

先给一个总框架:

高并发接口设计,本质上就是“前面挡住流量,中间快速处理,后面避免阻塞,异常时还能兜住”。

通常我会从这 8 个方面来设计:

  1. 接口分层与无状态化
  2. 限流与流量控制
  3. 缓存与热点处理
  4. 异步削峰
  5. 幂等与防重复提交
  6. 数据一致性控制
  7. 降级、熔断和隔离
  8. 监控、压测和容量评估

三、第一步:接口先无状态,方便横向扩容

高并发接口首先要求服务实例可以横向扩容。

应用层可以设计成:

  • 无状态服务
  • 请求通过 Nginx / Gateway / SLB 打到多个应用节点
  • 节点可以随时扩容和缩容

这样高峰期可以快速扩机器。

比如一个抢购接口:

  • 10 台服务扛不住,就临时扩到 30 台
  • 流量通过负载均衡自动分发

这一步是高并发的基础。


四、第二步:入口限流,先保护系统

高并发接口不能让所有请求都直接打到后端,否则数据库和服务会被冲垮。

所以一定要在入口做限流。

常见限流位置有三层:

1. 网关限流

在 Nginx、Spring Cloud Gateway、OpenResty 这一层限流。

比如:

  • 单 IP 每秒最多 50 次请求
  • 单用户每秒最多 5 次请求
  • 某活动接口每秒最多 1 万次请求

这一步是第一道保护。

2. 应用级限流

在服务内部再做更细颗粒度控制。

例如:

  • 按用户ID限流
  • 按商品ID限流
  • 按接口维度限流

常用算法有:

  • 令牌桶
  • 漏桶
  • 滑动窗口
  • 固定窗口

如果面试官追问,我一般会说:

网关层适合粗粒度全局保护,应用层适合业务级精细限流,两层结合效果最好。

3. Redis 分布式限流

如果是多实例部署,本地限流不够,要用 Redis 做全局限流。

比如 Lua + Redis 实现滑动窗口限流。


五、第三步:缓存前置,减少对数据库的冲击

如果这个高并发接口是读接口,比如查询商品详情、查询库存、查询活动信息,那第一选择一定是缓存。

1. Redis 缓存

热点数据直接放 Redis:

  • 商品详情
  • 活动信息
  • 库存余量
  • 用户资格信息

接口先查 Redis,只有缓存失效才打数据库。

2. 本地缓存

对于极热点数据,可以再加一层本地缓存,比如 Caffeine。

路径变成:

本地缓存 → Redis → DB

这样能进一步减少 Redis 压力。

3. CDN / 页面静态化

如果是活动页、商品详情页这种大量静态读流量,可以直接:

  • 静态化 HTML
  • CDN 分发
  • 前端缓存

这样大量请求甚至都到不了应用服务。


六、第四步:写接口不能硬扛,要削峰填谷

如果是下单、抢券、报名这种写接口,高并发下不能每个请求都同步落库,否则数据库肯定顶不住。

这时候要做异步化削峰

典型做法:MQ 排队

比如抢券接口:

  1. 用户请求先做资格校验
  2. 校验通过后,不直接操作数据库
  3. 请求写入消息队列
  4. 消费者异步处理发券逻辑
  5. 用户先拿到“排队中”或“抢券结果稍后通知”

这样做的好处是:

  • 把瞬时流量压平
  • 保护数据库
  • 提升系统稳定性

常用 MQ:

  • RocketMQ
  • Kafka
  • RabbitMQ

如果是交易场景,我更倾向 RocketMQ,因为它在顺序消息、事务消息、延迟消息上比较常见。


七、第五步:幂等设计,避免重复请求把数据打乱

高并发下最常见的问题之一,就是用户重复点击、网络重试、消息重复投递。

所以接口必须做幂等。

常见幂等方案

1. 前端防重

按钮置灰、提交后 loading,这只是第一层。

2. Token 防重

请求前先生成一个唯一 token,提交时校验并删除。

3. Redis setnx

比如:

  • 用户 + 业务ID 作为 key
  • SETNX 抢占
  • 成功才允许处理

4. 数据库唯一约束

比如下单接口里:

  • 同一个业务单号只能生成一次订单
  • 数据库层加唯一索引兜底

5. 消息消费幂等

MQ 消费时,通过业务流水号去重,保证重复消息不会重复处理。

面试里这句很关键:

高并发接口不怕重复请求,怕的是重复请求被重复处理。


八、第六步:热点资源要单独处理

高并发接口最怕“热点”。

比如:

  • 一个爆款商品 ID
  • 一个秒杀活动
  • 一个热门直播间
  • 一个抢券模板

这些热点会把流量集中到单个 key、单条数据、单个分区上。

处理方法

1. 热点缓存

把热点数据提前预热到 Redis。

2. 热点隔离

把热点接口、热点商品流量单独隔离处理,不和普通流量混用。

3. 分片/分桶

比如库存不要只放一个 key,可以拆成多个桶,降低并发争抢。

4. 本地缓存兜底

热点信息优先走 JVM 本地缓存。


九、第七步:数据库层一定要减压

高并发接口设计不好,最后都是数据库扛雷。

所以数据库层要尽量减压。

1. 读写分离

读请求走从库,写请求走主库。

2. 分库分表

如果单表数据太大,必须拆。

例如订单、优惠券领取记录、活动参与记录。

3. 批量写

异步场景下尽量批量 insert / update,减少数据库交互次数。

4. 乐观锁/CAS

如果有并发更新,比如库存扣减,可以用:

  • version 乐观锁
  • 条件更新

例如:

update stock
set available = available - 1
where sku_id = ? and available > 0

如果要更稳,可以加 version 字段。

5. 避免长事务

高并发接口里,事务一定要短,不能查很多表、调很多外部服务后再提交事务。

原则是:

核心写库逻辑放事务内,非核心操作异步化。


十、第八步:高并发写接口要注意一致性

很多人设计高并发接口只讲性能,不讲一致性,这是不够的。

比如下单接口,可能同时涉及:

  • 订单服务
  • 库存服务
  • 支付服务
  • 优惠券服务

这时候不能指望一个本地事务搞定,通常是:

  • 本地事务 + MQ 最终一致性
  • 或者 TCC
  • 或者可靠消息 + 补偿机制

如果是普通高并发接口,我一般优先选:

本地事务 + 异步消息 + 定时补偿

因为这个方案在性能和复杂度之间比较平衡。


十一、第九步:一定要有降级、熔断、隔离

高并发接口不是“尽量都处理”,而是“核心链路活着最重要”。

1. 降级

比如下单接口中:

  • 推荐商品可不返回
  • 个性化标签可关闭
  • 非关键埋点异步化

核心链路先保住。

2. 熔断

如果依赖的下游服务异常,比如营销服务超时:

  • 可以快速失败
  • 或走默认值
  • 防止把主链路拖死

3. 隔离

线程池隔离、服务隔离、流量隔离都很重要。

例如:

  • 会员接口和下单接口不要共用同一线程池
  • 热门活动和普通活动分开处理

十二、第十步:接口返回要分同步结果和异步结果

高并发接口不一定都要同步告诉用户“成功”或“失败”。

很多时候更合理的方式是:

  • 先返回“请求已受理”
  • 后台异步处理
  • 用户通过轮询、回调、站内信查看最终结果

比如:

  • 抢券:返回“排队中”
  • 秒杀:返回“抢购请求已提交”
  • 大促下单:返回“订单处理中”

这样能显著降低接口耗时和系统压力。


十三、如果让我举一个具体案例

比如设计一个高并发抢优惠券接口,我会这样设计:

1. 请求入口

  • 网关限流
  • 用户登录校验
  • 黑名单和风控校验

2. 资格预校验

先查 Redis:

  • 券是否存在
  • 活动是否开始
  • 库存是否还有
  • 用户是否已经领过

3. Lua 脚本原子判断

通过 Redis Lua 一次性完成:

  • 判断库存
  • 判断是否重复领取
  • 扣减库存
  • 写入领取标记

这样避免多次 Redis 往返,也保证原子性。

4. 异步入队

抢到资格的用户写入 MQ。

5. 消费端落库

消费者异步执行:

  • 写领取记录
  • 发放优惠券
  • 更新用户券包

6. 失败补偿

如果消费失败:

  • 重试
  • 死信队列
  • 人工补偿或自动回滚库存

7. 结果查询

用户可通过查询接口查看最终是否领券成功。

这个方案能很好地支撑高并发,而且相对稳。


十四、面试回答

我会先判断这个接口是读多写少还是写多读少。
如果是读接口,我会优先用多级缓存、本地缓存、Redis 和 CDN 减少对数据库的访问。
如果是写接口,我会重点做限流、削峰、异步化和幂等控制,避免所有请求直接打到数据库。
整体上,我会把服务设计成无状态方便横向扩容,在网关和应用层做多级限流,在 Redis 做热点数据缓存和原子判断,在 MQ 做异步削峰,在数据库层做读写分离、分库分表和短事务控制。
同时,为了保证系统稳定,我还会补充降级、熔断、隔离、监控告警、压测和容量评估。
如果这个接口涉及库存、订单、支付等多个服务,我会采用本地事务加消息最终一致性的方案来保证数据正确。


十五、总结

高并发接口设计的核心不是“让所有请求都进来”,而是:

让真正有效的请求尽快处理,让无效请求尽早拦截,让核心链路始终可用。