一、问题背景
当大量请求同时进入系统时,业务服务可能产生大量数据库查询,导致数据库 CPU 升高、连接池耗尽、SQL 执行变慢,甚至服务不可用。
例如:
- 系统突然收到每秒 10,000 个请求。
- 数据库在当前业务查询负载下只能稳定承受每秒 2,000 次查询。
- 如果不限制流量,大量请求可能持续访问数据库,导致数据库过载。
解决思路:在请求进入业务服务之前进行入口限流,减少进入后端的请求量。
注意:请求 QPS 不等于数据库查询 QPS,一个请求可能触发多条 SQL,也可能命中缓存。
二、方案一:Spring Cloud Gateway 限流
1. 适用场景
适用于 Java、Spring Cloud 微服务项目,需要在统一入口控制请求流量的场景。
2. 工程流程
客户端请求 → Spring Cloud Gateway → 限流检查 → 业务服务 → 数据库。
3. 具体实施步骤
第一步:制定限流规则。
根据接口的重要程度和数据库承载能力设置规则,例如:
- 单个用户每秒最多请求 10 次。
- 某个查询接口全局每秒最多放行 200 次。
- 对复杂报表查询设置更严格的限制。
具体阈值需要通过压测和线上监控确定。
第二步:接入限流组件。
使用 Spring Cloud Gateway 提供的 RequestRateLimiter 过滤器,配合 Redis 限流器实现请求速率限制。
主要配置包括:
KeyResolver:确定限流维度,例如用户 ID。RedisRateLimiter:配置令牌桶的填充速率和容量。RequestRateLimiter:在请求转发前执行限流检查。
第三步:处理超限请求。
- 未超过限制:转发到业务服务。
- 超过限制:拒绝请求,返回 HTTP 429。
- 根据业务需要返回统一错误码和友好提示。
第四步:压测与监控。
观察网关请求量、拒绝率、响应时间、数据库 QPS 和连接池使用情况,调整限流规则。
4. 优缺点
优点:
- 统一管理多个微服务的入口流量。
- 与 Spring Cloud 体系集成方便。
- 可以按用户或接口配置规则。
缺点:
- 需要部署和维护网关。
- 仅限制单个用户不能保证数据库安全,还需要接口级全局限制。
- 使用 Redis 限流时,需要考虑 Redis 的可用性和访问开销。
三、方案二:Nginx 限流
1. 适用场景
适用于已有 Nginx 作为反向代理或 HTTP 入口的系统,适合快速实现基础流量保护。
2. 工程流程
客户端请求 → Nginx → 限流检查 → 后端业务服务 → 数据库。
3. 具体实施步骤
第一步:确定需要保护的接口。
例如,复杂报表接口 /api/report/query 会执行成本较高的 SQL,需要设置单独的限流规则。
第二步:配置限流规则。
使用 Nginx 的 limit_req_zone 和 limit_req:
http { limit_req_zone $binary_remote_addr zone=report_limit:10m rate=5r/s;
server { location /api/report/query { limit_req zone=report_limit burst=10; limit_req_status 429;
proxy_pass http://backend; } }}配置含义:
limit_req_zone:创建限流区域,按客户端 IP 记录请求速率。rate=5r/s:平均速率限制为每秒 5 个请求。burst=10:允许一定程度的短时突发请求。limit_req_status 429:超限时返回 HTTP 429。proxy_pass:通过限流检查后才转发请求。
默认情况下,突发请求可能被延迟处理;如果配置 nodelay,允许的突发请求可以立即处理,但不会改变长期速率限制。
第三步:验证效果。
通过压测确认超限请求被拒绝,并观察后端请求量和数据库负载是否下降。
4. 优缺点
优点:
- 配置相对简单。
- 性能开销较低。
- 可以在请求进入应用前进行拦截。
缺点:
- 原生规则主要围绕 IP、请求速率等维度,业务身份识别能力有限。
- 多个 Nginx 实例独立计数时,不会自动形成统一的全局额度。
- 需要正确处理反向代理和客户端 IP,避免限流规则失效。
四、方案三:Redis + Lua 分布式限流
1. 适用场景
适用于多个应用实例需要共享限流额度,或者需要灵活实现用户级、接口级业务限流的场景。
2. 工程流程
客户端请求 → 网关或业务服务中的限流代码 → Redis + Lua 检查额度 → 放行或拒绝。
关键点:Redis 负责记录和判断额度,真正执行拦截的是调用 Redis 的代码。
如果在网关中调用 Redis,属于入口限流;如果在业务服务中调用 Redis,则请求已经进入应用,不能完全替代网关防护。
3. 具体实施步骤
第一步:确定限流维度。
例如:
- 单用户每秒最多请求 5 次。
- 整个报表接口每秒最多放行 500 次。
前者防止单个用户占用过多资源,后者控制接口总流量。
第二步:设计 Redis Key。
例如:
rate:user:1001:report:windowrate:global:report:window分别记录用户级和接口全局级的请求额度。实际实现中,需要正确设计时间窗口或令牌桶状态。
第三步:使用 Lua 原子检查。
将计数更新、额度检查和放行判断放在同一个 Lua 脚本中执行,避免并发请求之间发生计数竞争。
常见算法包括:
- 固定窗口:实现简单,但窗口边界可能出现突发流量。
- 滑动窗口:更精细地控制时间范围内的请求数量。
- 令牌桶:允许一定程度的突发流量,同时限制长期请求速率。
第四步:根据判断结果处理请求。
- 额度充足:放行请求。
- 超过限制:返回 HTTP 429。
- Redis 不可用:根据业务重要性制定故障策略,不能不加判断地全部放行。
第五步:保证集群运行稳定。
考虑 Redis 高可用、故障切换、网络延迟和限流状态的一致性。
4. 优缺点
优点:
- 限流规则灵活。
- 多个实例可以共享限流额度。
- 适合用户级和全局业务级限流。
缺点:
- 需要维护 Redis 和限流代码。
- 每次判断会产生 Redis 访问开销。
- Redis 故障可能影响请求处理。
- 应放在合适的入口位置,避免请求已经占用大量业务资源才开始限流。
五、方案四:云 API 网关 / WAF 限流
1. 适用场景
适用于部署在云上的系统,希望使用托管式流量管理和安全防护能力的场景。
2. 工程流程
客户端请求 → 云 API 网关或 WAF → 云上业务服务 → 数据库。
3. 具体实施步骤
第一步:统一接入云端入口。
让客户端请求通过云 API 网关进入后端,避免客户端绕过网关直接访问业务服务。
第二步:配置流量和安全规则。
例如:
- 按接口设置请求速率上限。
- 按账号、用户或 IP 设置请求配额。
- 对异常流量配置拦截规则。
- 对不同业务设置不同的流量额度。
需要注意:API 网关主要负责 API 管理与流量控制;WAF 更侧重 Web 攻击检测和恶意流量防护。WAF 不能替代数据库容量保护。
第三步:设置超限处理。
根据云产品的功能设置请求拒绝、错误响应和告警等动作。
第四步:验证防护效果。
观察超限请求是否被拦截、后端请求量是否下降,以及数据库 CPU、连接数和查询延迟是否改善。
4. 优缺点
优点:
- 减少自建中间件的部署和维护工作。
- 可以利用云平台已有的流量管理和安全能力。
- 适合云上业务的统一入口管理。
缺点:
- 依赖云厂商的产品能力和计费规则。
- 复杂的业务级限流可能仍需应用逻辑或 Redis 配合。
- 必须避免后端存在绕过入口的访问路径。
六、四种方案对比
| 方案 | 主要特点 | 适合场景 |
|---|---|---|
| Spring Cloud Gateway | 微服务统一入口、规则灵活 | Java 微服务 |
| Nginx | 配置简单、入口拦截开销较低 | 已有 Nginx 的系统 |
| Redis + Lua | 共享额度、业务规则灵活 | 分布式业务限流 |
| 云 API 网关 / WAF | 托管式流量与安全防护 | 云上系统 |
四种方案不一定互斥。例如,可以使用 Nginx 做基础限流,在 Spring Cloud Gateway 中做业务级限流,并通过 Redis 共享额度。
但不必一开始就全部部署,应根据现有架构、实际瓶颈和维护成本选择。
七、针对数据库过载的推荐方案
如果是 Java 微服务项目,可以采用以下分层防护方案:
- 入口层:网关限流。 限制用户级请求频率和接口总流量,超限请求直接返回 429。
- 共享限流层:Redis。 为多个网关实例提供统一额度,避免每台网关各自计数导致总流量超标。
- 应用层:并发控制。 使用线程池、并发信号量等机制限制同时执行数据库操作的任务数量。
- 数据访问层:连接池保护。 设置最大连接数、获取连接超时和合理的查询超时,避免连接耗尽。
- 数据库层:SQL 优化与监控。 排查慢 SQL、检查索引和执行计划,必要时使用缓存、读写分离等措施。
注意:连接池限制的是数据库连接数量,不等于限制每秒查询次数;入口 QPS 也不能直接等同于数据库 QPS。
八、面试口播答案
如果面试官问:“数据库流量过大时,网关入口限流有哪些工程实现?”
可以这样回答:
网关入口限流常见有四种工程实现。第一种是 Spring Cloud Gateway 自带的限流组件,适合 Java 微服务项目,可以按用户或接口设置请求上限;第二种是 Nginx 限流,通过请求速率限制在 HTTP 入口拦截超限请求;第三种是 Redis 配合 Lua 实现分布式限流,让多个实例共享用户额度和接口额度;第四种是使用云厂商提供的 API 网关或 WAF,利用托管式流量管理和安全防护能力。
针对数据库流量过大的场景,我会优先在入口层限制请求流量,同时通过 Redis 统一协调全局额度,再在应用层控制数据库访问并发,并使用连接池限制最大连接数。最后结合压测、数据库 QPS、慢 SQL、连接池使用率和请求拒绝率持续调整阈值,形成多层防护。
记忆主线:入口限流 → 共享额度 → 应用并发控制 → 连接池保护 → 数据库优化。
相关文章
- 数据库流量过大:中间件防护与数据库保护:本文所属的总览,串起入口限流、队列削峰和数据库自身保护。
- 方案一:Spring Cloud Gateway 入口限流:本文方案一的完整工程实施细节。
- 方案三:Redis + Lua 分布式限流:本文方案三的完整工程实施细节。