应存入独立数据库表thinkphp_access_log,含date(date)、route(varchar)、count(int unsigned)字段,并建联合索引idx_date_route;通过中间件无侵入记录,配合每日定时任务聚合到汇总表以保障查询性能与数据可靠性。

接口请求日访问量统计该存哪里
直接写数据库表比 Redis 更稳妥,尤其当你要做报表、导出或关联用户行为分析时。thinkphp_access_log 表结构至少得包含:date(DATE 类型,如 '2024-06-15')、route(VARCHAR,如 'api/v1/user/info')、count(INT UNSIGNED,默认 1)。别用 created_at + GROUP BY,日期字段单独建索引,查询快得多。
常见错误是把所有请求塞进一个大日志表然后每天 COUNT(*),结果一个月后 SELECT COUNT(*) FROM access_log WHERE DATE(created_at) = '2024-06-15' AND route = 'xxx' 就开始卡——因为 DATE(created_at) 无法走索引。必须拆出 date 字段并建联合索引:KEY idx_date_route (date, route)。
如何在 ThinkPHP 中无侵入式记录
用中间件最干净,不污染控制器逻辑。新建 app/middleware/AccessLog.php,在 handle() 里判断是否为 API 路由(比如前缀含 api/ 或路由分组标记),再调用 Db::name('access_log')->where(...)->inc('count')->strict(false)->execute() 实现原子自增;失败则 fallback 到 insert。
- 路由识别建议用
Request::url()或Route::current()->getName(),避免依赖__FUNCTION__这类不可靠来源 - 别在中间件里做耗时操作,
Db::execute()前加if (in_array($request->method(), ['GET', 'POST']))过滤掉 OPTIONS 预检 - 生产环境务必关闭调试模式下的 SQL 日志,否则每条请求都记两条日志(一条业务,一条统计)
统计结果怎么查才不拖垮数据库
不要等前端点“查看昨日TOP10”时才 SELECT route, SUM(count) FROM access_log WHERE date = '2024-06-15' GROUP BY route ORDER BY SUM(count) DESC LIMIT 10 —— 单表百万行时会慢。提前聚合好:每天凌晨用命令行任务跑一次 php think access:sync-daily,把当日明细合并进 access_daily_summary 表(字段:date、route、total、max_concurrent 等)。
这个命令本质就是一条 INSERT ... SELECT ... ON DUPLICATE KEY UPDATE,利用唯一联合索引 (date, route) 自动去重更新。关键点在于:执行时间避开业务高峰,且 SQL 中显式指定 STRAIGHT_JOIN(MySQL)或禁用自动重写,防止优化器选错驱动表。
缓存和并发怎么处理
高并发下多进程同时对同一 (date, route) 执行 inc('count') 可能漏计数。ThinkPHP 的 inc() 底层是 UPDATE ... SET count = count + 1,MySQL 行锁能保证原子性,但前提是主键或唯一索引存在且被命中。如果漏建索引,会升级成表锁,QPS 上千就卡住。
更稳的做法是加一层轻量缓存兜底:
- 先尝试
Cache::inc("access:2024-06-15:api/v1/user/info", 1, 3600) - 每分钟定时脚本把缓存里的 key 批量写回数据库,清空缓存
- 注意缓存 key 命名要统一,别混用
/api/v1/user/info和api/v1/user/info/(结尾斜杠差异)
真实线上环境里,数据库写入延迟几秒可接受,但不能丢数据;缓存只是提速手段,不是最终存储。别为了“看起来快”跳过落库步骤。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











