Laravel Yajra Datatables加载超1万条数据缓慢的优化咨询
万级数据Yajra Datatables加载慢优化方案
1. 核心问题修复:开启服务端渲染模式
当前最大的性能损耗是关闭了服务端分页,前端一次拉取全量1万条数据,且控制器提前查询全量数据转数组,完全浪费了Yajra Datatables的服务端处理能力。
修改前端代码
取消serverSide的注释:
$('.playertable').DataTable({ "language": { "url": "//cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/"+lag }, processing: true, serverSide: true, // 取消注释开启服务端模式 responsive: true, searching: true, "order": [[ 5, "desc" ]], ajax: { url: "{{ route('getplayerlist') }}", type: 'GET', data: function (d) { // 自定义搜索参数可在这里补充 } }, columns: [ {data: 'games', name: 'games'}, {data: 'player_no', name: 'username'}, {data: 'game_username', name: 'gameid'}, {data: 'balance', name: 'balance'}, {data: 'phone_number', name: 'phone'}, {data: 'created_at', name: 'createdate'}, {data: 'status', name: 'status'}, {data: 'action', name: 'action', orderable: false, searchable: false, 'className': "dt-center"}, ] });
修改控制器代码
不要提前执行get()查询全量数据,直接把查询构造器传给Datatables::of():
public function getPlayerlistAction(Request $request) { if ($request->ajax()) { // 直接传QueryBuilder,不要执行get()获取全量数据 $playerQuery = Player::where('parent_id', Auth::user()->id) ->with(['getGames' => function ($q){ $q->select('id', 'title', 'display_name'); }]) ->orderBy('created_at', 'desc'); return Datatables::of($playerQuery) ->addColumn('games', function ($row) { return $row->getGames->display_name; }) ->addColumn('phone_number', function ($row) { return $row->phone_number == '000' ? '-' : $row->phone_number; }) ->addColumn('balance', function ($row) { return number_format($row->balance, 2); }) ->addColumn('status', function ($row) { if ($row->status == 1) { return '<span class="badge badge-pill badge-success">' . __('mainlanguage.enabledtxt') . '</span>'; } return '<span class="badge badge-pill badge-danger">' . __('mainlanguage.disabledtxt') .'</span>'; }) ->addColumn('action', function ($row) { $actionBtn = '<span><button class="btn btn-primary btn-sm mt-1 mr-1 setscrore" data-gameusername="' . $row->game_username . '" data-game="' . $row->getGames->title . '" data-username="' . $row->player_no . '" data-gid="' . $row->games_id . '"><i class="fa fa-credit-card"></i> ' . __('mainlanguage.setscoretxt') . '</button>'; $actionBtn .= '<button class="btn bg-orange color-palette btn-sm mt-1 mr-1 resetpwd" data-gameusername="' . $row->game_username . '" data-gid="' . $row->games_id . '" data-username="'.$row->player_no.'" ><i class="fa fa-unlock"></i> ' . __('mainlanguage.resetpasstxt') . '</button>'; if($row->status == 1){ $actionBtn .= '<button class="btn btn-danger btn-sm mt-1 mr-1 disable" datatype=0 data-gameusername="' . $row->game_username . '" data-gid="' . $row->games_id . '" data-username="' . $row->player_no . '"><i class="fa fa-ban"></i> ' . __('mainlanguage.disabletxt') . '</button>'; }else{ $actionBtn .= '<button class="btn btn-success btn-sm mt-1 mr-1 disable" datatype=1 data-gameusername="' . $row->game_username . '" data-gid="' . $row->games_id . '" data-username="' . $row->player_no . '"><i class="fa fa-ban"></i> ' . __('mainlanguage.enabledtxt') . '</button>'; } $actionBtn .= '<button class="btn bg-teal color-palette btn-sm mt-1 mr-1 gamelog" data-gamename="'.$row->getGames->title.'" data-gameusername="' . $row->game_username . '" data-gid="' . $row->games_id . '" data-username="' . $row->player_no .'"><i class="fa fa-scroll"></i> ' . __('mainlanguage.gamelogtxt') . '</button>'; $actionBtn .= '<button class="btn bg-maroon color-palette btn-sm mt-1 mr-1 transactionlog" data-gameusername="' . $row->game_username . '" data-gid="' . $row->games_id . '" data-username="' . $row->player_no .'"><i class="fa fa-book"></i> ' . __('mainlanguage.tranactilogtxt') . '</button></span>'; return $actionBtn; }) ->rawColumns(['games', 'balance', 'status', 'action', 'phone']) ->make(true); } }
修改后每次只会查询当前页的10/20条数据,接口响应速度可降至100ms以内。
2. 数据库索引优化
当前的查询条件是where parent_id = ? order by created_at desc,需要给players表加parent_id + created_at联合索引,单独加单字段索引不会生效:
ALTER TABLE `players` ADD INDEX `idx_parent_created` (`parent_id`, `created_at` DESC);
3. 进一步性能优化(可选)
- 把字段格式化、HTML拼接逻辑移到前端处理,服务端只返回原始数据,减少服务端循环计算开销。比如balance格式化、status状态标签、操作按钮都可以在前端columns的render函数里生成。
- 如果数据实时性要求不高,可以给查询结果加5-10分钟的缓存,相同用户重复请求不用重复查库。
- 只查询需要的字段,在Player查询里加
select方法,明确指定要返回的字段,避免查询无用大字段占用内存。
内容的提问来源于stack exchange,提问作者ventures 999
相关产品推荐
相关产品推荐

