如何使用query函数解决Laravel DataTable性能缓慢问题?
Laravel DataTable 性能优化方案
问题根源
你用Audit::select()和Audit::query()->select()本质完全一致,都是生成相同的查询构造器,所以不会带来任何性能提升。性能慢的核心原因大概率是索引缺失、查询数据量过大或者DataTable未启用服务端分页。
具体优化步骤
1. 强制添加必要索引
这是最直接有效的优化手段,先检查数据库索引情况:
- 确保
audits.user_id字段有索引(关联查询的核心关联键) - 给
audits.created_at、audits.event这类DataTable常用的筛选/排序字段加索引 users.id默认是主键(已有索引,无需重复添加)
执行以下SQL添加索引:
-- 给audits.user_id添加单独索引 CREATE INDEX idx_audits_user_id ON audits(user_id); -- 给常用筛选字段添加复合索引(如果经常按时间+事件组合筛选) CREATE INDEX idx_audits_created_event ON audits(created_at, event);
2. 精简查询字段
如果DataTable不需要显示old_values和new_values(这两个通常是大体积JSON字段),直接从select中移除,能大幅减少数据传输和内存占用:
$audits = \OwenIt\Auditing\Models\Audit::select( 'audits.id', 'audits.user_type', 'audits.user_id', 'audits.event', 'audits.auditable_id', 'audits.auditable_type', 'audits.created_at', 'users.login' )->join('users', 'users.id', '=', 'audits.user_id');
注意:原join语句缺少
=符号,虽然Laravel会自动补全,但建议显式写出,避免语法歧义。
3. 启用DataTable服务端处理
这是Laravel DataTable性能优化的核心,不要把全量数据查出来交给前端处理,而是让DataTable通过AJAX请求分页加载数据(以Yajra DataTables为例):
- 创建DataTable服务类:
namespace App\DataTables; use OwenIt\Auditing\Models\Audit; use Yajra\DataTables\Html\Column; use Yajra\DataTables\Services\DataTable; class AuditDataTable extends DataTable { public function query(Audit $model) { return $model->newQuery() ->select( 'audits.id', 'audits.user_type', 'audits.user_id', 'audits.event', 'audits.auditable_id', 'audits.auditable_type', 'audits.created_at', 'users.login' ) ->join('users', 'users.id', '=', 'audits.user_id'); } protected function getColumns() { return [ Column::make('id'), Column::make('login')->title('操作人'), Column::make('event')->title('操作类型'), Column::make('created_at')->title('操作时间'), // 其他需要展示的列... ]; } // 可添加筛选按钮、排序规则等配置... }
- 控制器中调用服务类:
public function index(AuditDataTable $dataTable) { return $dataTable->render('audits.index'); }
这样DataTable会自动处理分页、筛选、排序逻辑,只查询当前页需要的10/20条数据,彻底避免全表扫描。
4. 关联查询优化
如果用户表数据量极大,可以通过子查询减少关联数据范围,比如只关联活跃用户:
$activeUsers = \App\Models\User::where('status', 1)->select('id', 'login'); $audits = \OwenIt\Auditing\Models\Audit::select( 'audits.id', 'audits.user_type', 'audits.user_id', 'audits.event', 'audits.auditable_id', 'audits.auditable_type', 'audits.created_at', 'users.login' )->joinSub($activeUsers, 'users', function ($join) { $join->on('users.id', '=', 'audits.user_id'); });
验证优化效果
优化后可以通过以下方式验证查询效率:
DB::enableQueryLog(); $audits->get(); dd(DB::getQueryLog());
查看生成的SQL是否使用了索引,避免出现全表扫描的情况。
内容的提问来源于stack exchange,提问作者Rodrigo Franco
相关产品推荐
相关产品推荐

