查数据库慢应先用DB::enableQueryLog()和dd(DB::getQueryLog())定位SQL问题,关注time和query字段,结合EXPLAIN分析索引与全表扫描,再优化N+1、缓存策略及外部调用。

查数据库慢,先看 DB::enableQueryLog() 和 dd(DB::getQueryLog())
Laravel API 慢,八成卡在查询上。别急着加缓存,先确认是不是 SQL 本身有问题——比如 N+1、没走索引、或一次性拉了太多字段。
- 在路由或控制器开头加
DB::enableQueryLog(),请求结束后用dd(DB::getQueryLog())看实际执行了哪些语句、耗时多少 - 注意
DB::getQueryLog()在生产环境默认关闭,且只对当前请求有效;不要在中间件里全局开,容易漏掉或干扰 - 重点盯
time字段(单位毫秒)和query内容:有没有重复出现的相同查询?有没有select *拉了 20 个字段但只用 2 个? - 配合 MySQL 的
EXPLAIN手动分析慢查询:把日志里的 SQL 复制出来,在数据库客户端前加EXPLAIN运行,看type是不是ALL(全表扫描)
Eloquent::with() 用不对,缓存也救不了 N+1
很多人以为加了 cache() 就万事大吉,结果缓存的是 100 条数据 + 100 次关联查询,反而更慢。
-
with()必须显式指定要预加载的关联,且只能用于已定义belongsTo/hasMany等关系的模型 - 避免嵌套过深:比如
with(['user.posts.comments'])容易触发笛卡尔积,优先拆成两步查,或改用load()按需加载 - 如果关联数据量大且不常变,可单独缓存关联结果,比如
Cache::remember("user_{$id}_posts", 3600, fn() => $user->posts),而不是缓存整个用户对象 - 慎用
withCount():它会生成子查询,大数据量下比with()更吃性能;真需要计数,考虑冗余字段或物化视图
Redis 缓存不是万能胶,Cache::remember() 的 key 设计决定命中率
缓存没效果?大概率是 key 冲突或粒度太粗,导致缓存写入了却从不被读到,或者一更新全崩。
- key 必须包含所有影响结果的变量:比如分页接口不能只用
"posts",得是"posts_page_{$page}_limit_{$limit}_sort_{$sort}" - 避免用动态 ID 做 key 主体(如
"user_{$id}"),而要用业务语义明确的标识(如"user_profile_{$id}"),否则容易和其他缓存混用 - 设置合理过期时间:用户资料可设 24 小时,实时订单状态最多 5 分钟;别全写
3600图省事 - 注意缓存穿透:查不到的数据也要缓存空值(如
Cache::remember($key, 60, fn() => $model ?? null)),并加?? null防止null被当成未命中反复查库
API 响应慢,可能根本不是 Laravel 的问题
有时候 profile 出来全是 Illuminate\Database\Connection::select() 耗时高,但真实瓶颈在外部服务或网络层。
- 检查是否在循环里调用外部 HTTP 请求(比如每条记录都
Http::get()查第三方 API),这种必须批量合并或异步处理 - 确认 Redis 或数据库连接是否复用:Laravel 默认用 PDO 长连接,但如果部署在 Serverless 或短命容器里,频繁重建连接会拖慢首字节响应
- 用
php artisan tinker单独跑一遍核心逻辑,排除框架启动开销;再用curl -w "@curl-format.txt" -o /dev/null -s http://api.test/xxx测真实 HTTP 层延迟 - 日志里出现
PDOException: SQLSTATE[HY000] [2002] Connection refused或Redis::connect(): php_network_getaddresses,说明连接池配置或网络策略有问题,缓存和查询优化都白搭
真正卡顿的地方,往往藏在「查了什么」和「谁在等谁」里。查日志、看连接、测单点,比直接改代码更省时间。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











