Redis实战:限制操作频率的实现方法
基于Redis实现操作频率限制的通用方案,从评论、点赞、上传等场景抽象出限制对象、时间范围、操作次数及禁止时间参数,利用Redis过期与计数器设计统一函数,控制超限后的提示与禁止。
最近一直忙于业务开发一线工作,博客更新有所搁置。接下来的计划是逐步分享从真实业务场景中沉淀下来的实战方案与设计思路,例如本文即将探讨的频率限制通用实现。不再追求一次性覆盖整个主题的大而全,而是侧重于具体问题的深入剖析与精细挖掘。通过持续的分享与复盘,将经验系统沉淀,同时保持稳定的输出节奏。
场景
场景1
留言功能限制,30秒内只能评论10次,超出次数不让再评论,并提示:过于频繁
场景2
点赞功能限制,10秒内只能点赞10次,超出次数后不能再点赞,并禁止操作1个小时,提示:过于频繁,被禁止操作1小时
场景3
上传记录功能,限制一天只能上传100次,超出次数不让再上传,并提示:超出今日上限
抽离本质
长期从事业务开发会发现,许多场景虽然打着不同的业务模块标签,但本质上解决的是同一个问题。例如上述几个场景——评论、点赞、上传——看似毫不相干,底层逻辑却高度相似。如果只是机械地为每个场景单独编写代码,那就成了俗称的“CV(复制粘贴)工程师”。更有价值的做法是:挖掘问题的共性,设计一套通用解决方案。这或许就是有灵魂的工程师与纯工具人之间的本质区别。
对上面三个场景进行分析,可以绘制出如下逻辑流程图:

进一步提炼,它们都依赖于以下几个条件:
- 限制对象:当前用户
- 限制操作:评论、点赞、上传等具体动作
- 时间范围:X秒内的允许窗口
- 限制操作数:Y次上限
- 超出后禁止操作时间:Z(秒或具体时间戳)
- 超出后的响应:拒绝操作并给出提示信息

如果将该功能抽离为通用函数,大致形式如下:
1,'ttl'=>过期时间/秒] ['type'=>2,'ttl'=>具体过期时间戳] 二选一
* @return bool
* @throws Exception
*/
public static function frequencyLimit(string $action, int $userId, int $time, int $number, $expire = [])
{
// todo 根据用户操作动作时间范围,进行频率的控制和失效释放
}
解决方案落地
功能核心需要记录用户每次操作的时间与累计次数,同时支持自动过期清理。如果依赖MySQL实现,频繁的读写操作和过期数据清理会带来较高的性能和维护成本。这时Redis成为理想方案——利用INCR的原子操作和Key的过期机制,结合内存存储的高效特性,Redis能简洁灵活地完成这项任务。
以下是通用功能的简易实现代码:
1,'ttl'=>过期时间/秒] ['type'=>2,'ttl'=>具体过期时间戳] 二选一
* @return bool
* @throws Exception
*/
public function frequencyLimit(string $action, int $userId, int $time, int $number, $expire = [])
{
if (empty($action) || $userId <= 0 || $time <= 0 || $number <= 0) {
throw new Exception('非法参数');
}
$key = 'act:limit:' . $action . ':' . $userId;
$r = RedisClient::connect();
// 获取当前累计次数
$current = intval($r->get($key));
if ($current >= $number) return false;
// 累计并返回最新值
$current = $r->incr($key);
// 第一次累加,设置控制操作频率的有效时间
if ($current === 1) $r->expire($key, $time);
// 未超出限制次数先放过
if ($current < $number) return true;
// 超出后根据需要重新设置过期失效时间,$current === $number 判断保证只重新设置一次
$type = empty($expire['type']) ? 0 : intval($expire['type']);
$ttl = empty($expire['ttl']) ? 0 : intval($expire['ttl']);
if ($current === $number && $ttl > 0 && in_array($type, [1, 2])) {
if ($type === 1) $r->expire($key, $ttl);
if ($type === 2) $r->expireAt($key, $ttl);
}
return false;
}
// 场景1:评论限制
public function doComment(int $userId)
{
try {
$pass = FrequencyLimit::doHandle('comment', $userId, 30, 10);
if (!$pass) return '过于频繁';
// todo 评论逻辑
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// 场景2:点赞限制
public function doLike(int $userId)
{
try {
$pass = FrequencyLimit::doHandle('like', $userId, 10, 10, ['type' => 1, 'ttl' => 1 * 60 * 60]);
if (!$pass) return '过于频繁,被禁止操作1小时';
// todo 点赞逻辑
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// 场景3:上传限制
public function doUpload(int $userId)
{
try {
$expire = strtotime(date('Y-m-d', strtotime('+1 days')));
$pass = FrequencyLimit::doHandle('upload', $userId, 1 * 24 * 60 * 60, 100, ['type' => 2, 'ttl' => $expire]);
if (!$pass) return '超出今日上限';
// todo 上传逻辑
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// 场景N……
总结
将相似的业务场景归集分析,识别本质问题并设计通用解决方案,远比每次重新造轮子更加高效。Redis的原子操作与过期机制在此场景中发挥了关键作用——INCR保证了并发安全,EXPIRE自动清理过期数据,代码简洁且性能出色。做一个有灵魂的开发者,从习惯抽象共性问题开始。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
AI如何从聊天能力进化到做事能力 Skill的实践之路
让AI从“会聊天”转向“会做事”的关键在于Skill(技能包)。Skill由可执行代码、精确的说明书(如JSONSchema描述)及结果翻译器构成。通过精细定义参数与边界,AI能稳定调用接口完成重启服务、查询数据等操作,从而从空谈顾问变为可靠的数字员工。
零基础看懂Agent Skill MCP三层关系解读
智能体是自主执行任务的实体,技能是被封装的原子能力,MCP协议是智能体与技能间的标准化通信协议。智能体通过MCP协议发现并调用技能,灵活组合完成复杂任务。三者解耦,实现即插即用,降低了系统集成复杂度。
AI编码时代UI自动化测试智能化演进之路—中国平安人寿蔡雪
AI编码时代,UI自动化测试面临效率断层。平安人寿蔡雪基于自研“女娲”平台,分享从可视化录制到AI智能录制、基于EventDOM的智能感知与自愈机制的演进路径,实现用例创建降本、维护减负、执行提稳,推动测试工具从自动化向智能化升级。
一文讲清Agent、Skill、MCP到底什么关系:零基础小白三层拆解
Agent是自主执行任务的数字打工人,Skill为原子化能力函数,MCP是标准协议接口。Agent通过MCP发现并调用Skill,实现即插即用,解耦技能与智能体,让大模型能力安全、统一、可扩展地集成。
文生图同一提示词为何每次不同?随机性与可复现解析
文生图每次结果不同源于从随机噪声开始去噪。固定种子(seed)可锁定初始噪声,但还需采样步数、引导强度、采样器、尺寸、提示词、模型等参数一致才能复现。通过控制变量法调参,先固定种子再逐一调整其他参数,可精确归因差异。
- 热门数据榜
相关攻略
2026-07-10 11:09
2026-07-10 11:09
2026-07-10 11:09
2026-07-10 11:09
2026-07-10 11:09
2026-07-10 11:08
2026-07-10 11:08
2026-07-10 11:08
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

