常见限流与控频策略对比分析
对四种限流策略进行对比:基于数据库实现滑动窗口,适合登录控制;基于Redis固定窗口,适合接口访问控制;基于内存滑动窗口,实现应用级限流;木桶算法滑动窗口,可根据应用位置灵活部署,能应对分布式环境。
本文将深入探讨几种常见的限流方案,每种方案都具备独特的适用场景。我们将从实现策略、控制粒度以及时间窗口的处理方式等维度进行详细分析。请注意,示例代码可能存在不够规范之处,核心目标是帮助理解设计思路。
1. 基于数据库统计的限流方法
基于数据库实现限流,核心思路非常直观:每次操作时,向数据库写入一条包含时间戳的记录,随后根据设定的时间范围进行统计,判断是否达到限流阈值。下面通过一个具体场景帮助理解。
适用场景:用户密码在1分钟内输入错误次数过多,系统自动冻结账号。
要点:
- 登录失败时,将登录名和登录时间写入数据库记录。
- 登录前,根据登录名查询最近1分钟内的失败次数。
- 判断失败次数是否超过预设阈值,若超过则冻结账号,否则执行正常登录流程(登录失败后再写入记录)。
代码要点:
select count(登录名) from 登录错误记录表 where 登录名='登录名' and 登录时间 > DATE_SUB(NOW(), INTERVAL 1 MINUTE)
分析:
该方案的粒度控制可通过代码逻辑与数据库设计灵活调整,时间窗口设为最近一分钟,属于典型的滑动时间窗口。由于依赖数据库存储,它同样适用于分布式环境。然而,缺点显而易见——随着请求量增加,数据库压力会急剧上升。
2. 基于Redis自增及过期策略的限流方案
基于Redis实现限流,核心思路如下:在Redis中维护一个带有过期时间的键(key),每次操作前先读取该键的值。如果当前值已超过限制,则触发限流;否则通过INCR命令进行自增,若键不存在,则创建并设置过期时间。如此循环往复。
适用场景:对接口实施会话级别的访问频率控制。
要点:
- 读取键值,根据value的不同情况分别处理:若键不存在,则设置键并指定过期时间;若value大于限定值则触发限流;若value未超过限定值则执行自增操作。
代码要点(Redis命令):
## 设置key的值5秒过期
set kkk 1 EX 5
### 获取key的值
get kkk
### 自增长
incr kkk
### 手动设置过期时间
expire kkk 5
代码示例:
private boolean accessCheck(HttpSession session) {
String key = "ACCESS" + session.getId();
long current = 1;
// key不存在,进行设置
if( valueOperations.get(key) == null){
// 一步到位
//valueOperations.set(key,1,5,TimeUnit.SECONDS);
// 也可以分两步设置
valueOperations.increment(key);
valueOperations.getOperations().expire(key,5,TimeUnit.SECONDS);
} else {
// key已经存在了,自增 1
valueOperations.increment(key);
current = Long.parseLong(valueOperations.get(key));
}
if(current > 20){
throw new RuntimeException("访问次数过多,请稍后再试");
}
return true;
}
分析:
Redis方案同样支持分布式环境,时间窗口为固定窗口(取决于设置的过期时间)。凭借Redis的高性能,该方案能够应对较大的流量冲击。控制粒度则取决于具体的设计细节。
3. 基于内存(以LinkedList为例)的限流方案
基于内存的限流策略,实质是在内存中维护一个时间窗口的数据结构,通过统计窗口内的请求数量来做出限流决策。
适用场景:对接口调用频率进行控制。
要点:
- 维护一个按时间升序排列的请求列表(避免每次全量扫描)。
- 移除超出时间窗口的旧数据。
- 在列表末尾添加最新的时间戳。
代码要点(以LinkedList为例):
private boolean accessCheckByLinkedList(){
synchronized(accessList) {
Date data = null;
Date now = new Date();
if(accessList.size() > 0){
data = accessList.getFirst();
while (data != null && now.getTime() - data.getTime() > 5000) {
accessList.removeFirst();
if(accessList.size() > 0){
data = accessList.getFirst();
} else {
data = null;
}
}
if (accessList.size() > 5) {
throw new RuntimeException("访问频率过高");
}
}
accessList.add(new Date());
}
return true;
}
分析:
内存限流的控制粒度完全取决于设计者的实现水平。由于是内存操作,必须妥善处理线程同步(虽然使用ConcurrentHashMap等并发容器可以减少部分同步问题,但排序仍需注意)以及高并发下的竞争条件。其控制粒度为应用级别,能够从应用层面直接拦截流量,避免因负载均衡策略失效或异常情况导致后端服务被冲垮。
4. 基于木桶算法的限流方案
木桶算法的思路更为形象:维护一个固定容量的“池”,每次请求消耗池中的存量,同时通过后台任务持续向池中添加资源,并确保存量始终不超过最大值。
适用场景:应用级别接口请求频率控制。
要点:
- 当增加池中存量时,若达到最大值则暂停新增。
- 当使用池中存量时,若存量降至0以下则触发限流。
- 确保池既不会被撑爆,也不会被透支。
代码要点:
public class BucketAlgorithmTest {
public static void main(String[] args) throws InterruptedException {
// 定时添加资源
new Thread(()-> {
while (true){
Bucket.add();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(200);
// 若初始默认值为满则可省略此步骤
// 定时消耗资源,模拟控制逻辑
new Thread(()->{
int errorcout = 0;
while (errorcout < 10) {
try {
Bucket.get();
Thread.sleep(40);
} catch (Exception e) {
errorcout++;
e.printStackTrace();
try {
Thread.sleep(100);
}catch (Exception ee){}
}
}
}).start();
}
}
class Bucket{
/**
* 当前可用量
*/
private static Integer currentSize = 0;
/**
* 最大容量
*/
private final static int totalSize = 20;
/**
* 添加资源
*/
public static void add(){
synchronized (currentSize) {
if(currentSize < totalSize) {
currentSize++;
System.out.println("[添加 ]可用" + Bucket.getCurrentSize());
}
}
}
/**
* 消耗资源
*/
public static void get(){
synchronized (currentSize) {
if(currentSize > 0) {
currentSize--;
System.out.println("[使用---]可用" + Bucket.getCurrentSize());
}else {
throw new RuntimeException("当前无可用资源,请求被限流");
}
}
}
public static Integer getCurrentSize(){
return currentSize;
}
}
为了方便理解,这里贴一张运行结果:

分析:
木桶算法的可调性很强,具体效果取决于对原理的理解深度。在突发大流量冲击下,恢复时间可能相对较长。它本质上是一种“滑动流量”窗口,而非严格的时间窗口。该策略的数据可以存储在内存、数据库或Redis中,应用场景也可灵活定制,既可作为应用级限流,也可作为负载均衡层面的策略补充。
限流方案对比总结
| 方案 | 时间窗口 | 基于 | 分布式环境 | 应用范围 |
|---|---|---|---|---|
| 数据库统计 | 滑动 | 数据库 | 支持 | 登录次数控制 |
| Redis自增 | 固定 | Redis | 支持 | 接口频率控制 |
| 内存列表 | 滑动 | 内存 | 不支持 | 应用级频率控制 |
| 木桶算法 | 滑动 | 内存/Redis/数据库 | 支持 | 灵活配置 |
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
Claude Code 必知的14个高效工作流,让你的开发效率提升300%
Claude Code 常用工作流 先分享几项核心判断:Claude Code 真正强大的地方,并非仅仅在于它能编写代码——而是它让“编码”这件事本身变得更加高效且可控。你大概率遇到过这类场景:接手一个陌生项目,花了一整天才能理清架构;线上出现报错,翻遍日志也找不到根本原因;想要重构遗留代码,又担心
阿里云通义AIGC平台完全指南:设计师AI生产力革命
一、写在前面:为什么设计师需要关注AIGC? 如果你还在手动一张一张制作海报、反复修改客户口中“感觉不对”的配色方案、为电商详情页准备几十张不同场景的产品图——那么你一定经历过这些痛点: 创意瓶颈:脑海中有画面,但手绘无法呈现 重复劳动:调整尺寸、更换背景、批量生成变体消耗了大量时间精力 成本焦虑:
零基础毕设代码二次开发:3文件定位法及Vue/Java修改对照表
每年一到毕业季,计算机专业的同学总会陷入一个共同的怪圈:从 GitHub 上扒下来一套代码,或者用 AI 生成一个项目,看起来挺完整的,可导师一句“加个筛选条件”或“换个页面颜色”,瞬间就懵了——不敢改,不会改,怕改崩。是不是很熟悉? 一、为什么AI生成的毕设代码你 "不敢改 "? 1 1 毕业生的三大
反向海淘订单系统:状态机与分布式事务实战设计
先分享一个反直觉的结论:反向海淘订单管理的真正挑战,往往不在于业务逻辑本身,而在于状态流转。一个订单的生命周期拉长到跨国运输,中间涉及的环节多、系统多、参与者多,状态稍有错乱就可能引发连锁事故。Taocarts团队在实践中踩了不少坑,最终沉淀下来的这套状态机与分布式事务方案,成功解决了这一复杂难题。
AI并未抢走程序员饭碗而是更新了编程菜单
AI并未大规模替代程序员,而是改变了职业结构。重复性编码岗位需求下降35%至15%,而AIAgent开发等岗位需求激增187%。开发者焦虑从“被替代”转向“跟不上变化”,60%程序员已使用AI辅助编程。人的核心价值转向架构设计、技术决策和审查AI生成代码,AI技能带来16%薪资溢价。
- 热门数据榜
相关攻略
2026-07-09 17:49
2026-07-09 16:48
2026-07-09 16:46
2026-07-09 16:46
2026-07-09 16:46
2026-07-09 16:46
2026-07-09 15:45
2026-07-09 15:45
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

