Laravel with()关联查询慢问题排查:5000条数据耗时5-10秒
嘿,我来帮你捋捋这个问题——你现在遇到的慢加载问题,大概率不是Laravel或者Laravel Datatables本身的锅,而是你的查询写法有优化空间,尤其是在处理关联数据和大数据量的时候。
问题根源
你当前的代码是先通过Lead::with([...])->get()把所有5000条Lead数据以及它们的关联数据一次性加载到内存中,然后再传给Datatables做分页。这就导致了两个核心问题:
- 数据库要一次性返回大量数据,传输和IO耗时极高;
- 服务器要把所有数据存入内存,占用资源大,后续Datatables的分页只是在内存集合上做过滤,完全没有利用数据库的原生分页能力。
哪怕你说插件会添加分页限制,那也是在全量数据加载完成之后的操作,慢查询的根源已经埋下了。
另外,还要排查关联查询的效率问题:如果关联表的外键没有加数据库索引,预加载的关联查询速度也会非常慢,尤其是嵌套关联assign.buyer这种多层关联。
具体优化方案
1. 让Datatables直接操作查询构造器(核心优化)
不要先get()出全量数据,而是把未执行的查询构造器传给Datatables。这样插件会自动在数据库层面做分页、排序、搜索,只会拉取当前页需要的几十条数据,而不是全量5000条。
修正后的代码:
// 只创建查询构造器,不执行get() $leadQuery = Lead::with(['vertical', 'website', 'source', 'agent', 'assign', 'assign.buyer', 'returns']); // 把查询构造器传给Datatables $datatable = datatables()->of($leadQuery); // 最后返回Datatables响应(不要用dd,否则看不到实际效果) return $datatable->make(true);
这样修改后,数据库只会查询当前页的几十条数据,查询速度会立刻大幅提升,哪怕后续数据涨到20万条也能轻松应对。
2. 添加数据库索引(关键优化)
打开你的数据库管理工具(比如phpMyAdmin、Navicat),检查以下外键字段是否添加了索引:
leads表的vertical_id、website_id、source_id、agent_idassigns表的lead_id(对应Lead的hasOne关联)、buyer_id(对应assign.buyer关联)assign_returns表的lead_id(对应Lead的hasMany关联)
如果没有索引,赶紧为这些字段创建索引——数据库索引能让关联查询的速度提升数倍甚至数十倍。
3. 只加载需要的字段(锦上添花)
如果你的Datatables不需要展示关联表的所有字段,可以在预加载的时候指定需要的字段,减少数据传输量和内存占用:
$leadQuery = Lead::with([ 'vertical:id,name', // 只取id和name字段 'website:id,url', 'source:id,title', 'agent:id,name', 'assign:id,lead_id,buyer_id', 'assign.buyer:id,name', 'returns:id,lead_id,reason' ]);
注意:指定字段时必须包含关联的外键(比如vertical:id,name里的id,因为Laravel需要用它来关联主表数据)。
4. 排查预加载是否生效(避免N+1问题)
虽然你用了with()做预加载,但还是要确认是否真的避免了N+1查询问题。可以通过Laravel的查询日志验证:
// 开启查询日志 DB::enableQueryLog(); // 执行你的查询逻辑 $leadQuery = Lead::with(['vertical', 'website', 'source', 'agent', 'assign', 'assign.buyer', 'returns']); datatables()->of($leadQuery)->make(true); // 打印查询日志 dd(DB::getQueryLog());
正常情况下,应该只有1条主表查询 + 每个关联1条查询(嵌套关联assign.buyer会多1条)。如果发现有大量重复的关联查询,说明预加载没生效,可能是模型关联的外键配置错误,比如belongsTo方法没有指定正确的外键字段(默认外键是关联模型名+_id,如果你的字段名不一样,需要手动指定)。
总结
你当前的核心问题是提前加载了全量数据,改成传递查询构造器给Datatables,再配合数据库索引优化,应该能把查询时间降到几百毫秒以内,完全能应对后续20万条数据的规模。
内容的提问来源于stack exchange,提问作者kjdion84

