cache::inc()是首选,因其自动适配驱动、保证原子性并内置ttl设置;而redis::incr()需手动处理连接、错误及过期逻辑,易导致key堆积。

直接用 Cache::inc() 就能做接口调用量的临时统计,但必须确保 Redis 驱动已正确加载、连接参数填全、key 设计合理——否则会静默降级到 file 缓存,你以为在计数,其实数据根本没进 Redis。
为什么 Cache::inc() 是首选而不是手写 Redis::incr()
ThinkPHP 的 Cache::inc() 底层自动适配当前缓存驱动(比如 redis),且自带原子性、TTL 设置和前缀处理;而直接调用 Redis::incr() 需手动管理连接、错误兜底、过期逻辑,还容易因未设置 EXPIRE 导致 key 永久堆积。更重要的是,Cache::inc() 在 TP8/TP6 中默认走 store 配置,只要 cache.php 里 redis 配置完整,它就真走 Redis。
常见错误现象:
-
Cache::inc('api:/v1/user')返回false或始终为1:大概率是缓存驱动 fallback 到了 file,检查日志里有没有RedisException或Connection refused - 统计值不递增:key 被不同中间件/控制器重复初始化,或用了带时间戳的动态 key(如
api:/v1/user:20260817),导致每次都是新 key
TP8 必须显式配置 redis 连接参数,.env 不生效
TP8 不会从 .env 自动读取 REDIS_HOST 等变量,只认 config/cache.php 里的硬编码配置。漏掉任何一项(尤其是 database 或 auth)都会让连接失败后悄悄切回 file 驱动。
正确写法(config/cache.php 中):
'stores' => [
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
'auth' => '', // ACL 模式下填用户名:密码,非 password 字段
'timeout' => 0,
'expire' => 0,
'prefix' => 'stat:',
],
],
'default' => 'redis',
注意:
-
database必须显式写成0,不能留空或注释掉 - 如果 Redis 启用了 ACL(6.0+),要用
auth而不是password,且该用户需有+set权限 -
prefix建议加,避免和其他业务 key 冲突,比如stat:api:/v1/user
接口统计 key 设计与中间件实操
统计逻辑应放在全局中间件里,统一拦截请求路径,避免每个控制器重复写。key 要包含路径 + 可选维度(如用户 ID),但不要嵌入秒级/毫秒级时间戳——有效期靠 TTL 控制更可靠。
示例中间件(app/middleware/ApiStatMiddleware.php):
use think\facade\Cache;
class ApiStatMiddleware
{
public function handle($request, \Closure $next)
{
$path = $request->url(true); // 或 $request->rule()->getRule() 获取路由名
$uid = $request->header('x-user-id', 0);
$key = 'api:' . ($uid ? "user_{$uid}:" : '') . $path;
Cache::store('redis')->inc($key, 1, 3600); // 1小时过期
return $next($request);
}
}
关键点:
- 用
Cache::store('redis')显式指定驱动,不依赖 default,防止配置错乱 -
inc($key, 1, 3600)第三个参数是 TTL(秒),不是“缓存有效时间”,它会在 Redis 层执行INCR+EXPIRE原子操作 - 不要用
$request->ip()作维度——高并发下 IP key 太散,容易撑爆内存;如需限流,改用令牌桶或滑动窗口
怎么验证 Redis 真正在工作?
最直接的方式是进 Redis CLI 查 key:
redis-cli -h 127.0.0.1 -p 6379 > keys "stat:api:*" > get "stat:api:/v1/user"
如果返回空或报错,说明没连上;如果返回数字但值不变,检查是否被其他逻辑覆盖(比如多个中间件对同一 path 调了多次 inc);如果 key 存在但没过期,确认 inc() 的第三个参数没传 0(0 表示永不过期)。
真正容易被忽略的是:TP8 的 Redis 连接失败不会抛异常,只会记一条日志然后静默降级——所以线上出问题时,第一反应不该是查代码,而是翻 runtime/log/ 里最近的 RedisException 日志。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











