结论是不要用log::write()统计接口频次,而应在中间件中用cache::inc()原子计数或db::insert()持久化;key需用路由name而非url,ttl设60秒实现滚动窗口,ip和uid须取真实值并过滤探针路径。

直接上结论:不要用 Log::write() 记录原始请求频次,它不带计数逻辑、无法原子递增、查不出来“每分钟调了多少次”。真要统计,得在中间件里用 Cache::inc() 或 Db::insert() 主动写,且 key 设计和时间粒度必须对齐业务目标。
用 Cache::inc() 做实时原子计数
这是高并发下最稳的方案,Redis 的 INCR 天然线程安全,不用读-改-写,也无需手动初始化。
-
$key = 'api:count:' . $request->rule()->getName();—— 优先用路由 name(如api_user_profile),别拼 raw URL,否则/user?id=1和/user?id=2会被算作两个接口 - 调用
Cache::inc($key, 1, 60)即可:第三个参数是 TTL,设为 60 秒表示“滚动窗口”,不是“固定每分钟清零” - 别把时间戳塞进 key 里,例如
api:count:2026081713:/user,这会导致窗口无法滑动,统计失真 - 如果需要按用户+IP 维度细分,用冒号分层:
api:count:user_'. $uid .':'. $ip .':'. $route,但注意 key 长度别超 512 字符
用 Db::insert() 做审计级持久化
适合要留痕、可回溯、需配合日志分析的场景,但不能扛高并发直写,得加缓冲。
- 建表字段至少含:
api_path(VARCHAR 255)、ip(VARCHAR 45)、created_at(DATETIME)、uid(INT UNSIGNED NULL) - 中间件里别直接
Db::name('api_call_log')->insert(...),QPS 超 200 就可能拖慢主库;改用think-queue推入队列,后台消费批量写入 - 如果非得同步写,至少加个简单开关:
if (env('APP_ENV') !== 'prod') { Db::... },避免开发环境刷爆日志表 - 查询当日各接口调用量时,用
whereTime('created_at', 'today')+group('api_path'),但注意count(*)返回的是字符串,前端用前得转(int)
中间件里怎么取准维度信息
很多统计不准,根源不在存储,而在埋点那一刻就错了——比如拿不到真实用户 ID,或把健康检查接口也记进去了。
- 用户 ID 别从
session('user_id')硬取,JWT 场景下应解析 token:$token = $request->header('Authorization'); $payload = \Firebase\JWT\JWT::decode(...); $uid = $payload->uid; - 过滤掉无意义路径:
in_array($request->path(), ['api/ping', 'healthz']) || $request->isStatic(),避免监控探针污染数据 - IP 要取真实客户端,不是 Nginx 的
127.0.0.1:$ip = $request->header('X-Real-IP') ?: $request->ip(); - 接口标识统一走
$request->rule()->getName(),确保路由定义时已配['name' => 'api_order_create']
导出数据给前端图表时的硬坑
Redis 数据能查出来,但 ECharts 渲染失败,90% 是因为类型没转、key 没收敛、或者一次拉太多被阻塞。
- 绝对别用
Cache::get('api:count:*')或KEYS api:count:*—— 生产环境会卡死 Redis 主线程 - 前端传参限定查询条件,后端只查单个:
Cache::get('api:count:' . input('route') . ':' . input('ip')) - Redis 返回的
count值永远是 string,后端响应里必须显式 cast:'count' => (int) $cacheValue,不能依赖 PHP 自动转换 - 如果要做折线图展示趋势,不要靠前端轮询多个 key,而应在定时任务里每分钟聚合一次,写到
stat:hourly:api_order_create这类 Hash 中,再由接口按需读取
真正难的不是写几行 Cache::inc(),而是所有服务节点用同一套 key 规则、所有中间件对同一维度(如 user_id)解析方式一致、所有定时任务清理逻辑和缓存 TTL 对得上。漏掉任意一环,数据就不可信。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











