中间件查数据库可行但需权衡代价,常见于权限校验、限流、白名单等动态决策场景;须确认上下文是否充足、是否必须实时、是否影响全局性能,并避免N+1与事务陷阱。

中间件里查数据库完全可行,但必须明确目的和代价
能查,而且很常见——比如权限校验要查用户角色表、API限流要读写计数器、请求白名单要匹配数据库记录。但查库行为会直接拖慢整个中间件链路,尤其在高频接口上,一个 DB::table('roles')->where('id', $user->role_id)->first() 就可能让 TTFB 多出 20ms。
查库前先确认这三件事
不是所有场景都适合在中间件里查库:
- 是否已有足够上下文?比如用户已登录且
$request->user()已加载,就别再用 ID 去查一遍用户表 - 是否必须实时?缓存过的角色权限(
Cache::get('role_permissions:'.$role_id))比直连数据库更稳 - 是否影响全局性能?如果这个中间件注册在
$middleware数组里(即全局中间件),每次请求都会执行,哪怕只是静态资源
避免 N+1 和事务陷阱
中间件里查库最容易踩两个坑:
- 在循环中查库:比如对每个请求头字段都去查配置表,应提前用
whereIn一次性拉取 - 在事务未结束时查库:若控制器里开启了事务,中间件里查的可能是旧快照数据;反过来,中间件里开启事务但没 commit/rollback,会导致后续操作阻塞
- 别在中间件里调用
DB::transaction()—— 它会包裹整个请求生命周期,极易锁表或超时
更推荐的替代方案
多数情况下,查库逻辑可以前置或后置:
- 用模型观察者(Observer)监听数据变更,把日志/同步等动作移出请求链路
- 把权限规则预加载进 session 或 JWT payload,中间件只做内存比对
- 用队列处理耗时查库动作(如审计日志写入),中间件只发任务
LogUserActivity::dispatch($request)
真正需要中间件查库的,往往是那些无法预判、必须动态决策的边界场景——比如按 IP 段限制访问、实时黑名单校验。这时候查库不是偷懒,而是必要设计。


















