【问题标题】:laravel datatable take long time to load datalaravel datatable 需要很长时间才能加载数据
【发布时间】:2019-12-20 01:46:45
【问题描述】:

我正在尝试使用以下代码将数据库表显示到 laravel 数据表,但 laravel 数据表需要很长时间才能加载数据。 我在数据库中有大约 700000 条记录。如何减少加载数据的时间?

代码

web.php

Route::get('home', 'HomeController@index')->name('home');

HomeController.php

public function index()
{
    $campaigns = TonicData::select('campaign')->distinct()->get();

    if (request()->ajax()) {
        $data = \DB::table('tonic_data')
            ->whereNotNull('subid4')
            ->where('subid4', '!=', '')
            ->select('subid4')
            ->groupBy('subid4')
            ->selectRaw('sum(view) as sum_of_views, sum(term_view) as sum_of_term_views,
                sum(add_click) as sum_of_add_click, sum(revenue_usd) as sum_of_revenue,
                (sum(revenue_usd)/sum(view)*1000) as rpm')
            ->when((request()->has('selectedCampaign') && request()->get('selectedCampaign') != ''), function ($query) {
                $query->whereIn('campaign', request()->get('selectedCampaign'));
            })
            ->when((request()->has('selectedDateRange') && request()->get('selectedDateRange') != ''), function ($query) {
                $query->whereBetween('day_date', [request()->get('selectedDateRange')['fromDate'], request()->get('selectedDateRange')['toDate']]);
            });

        return DataTables::of($data)
            ->addIndexColumn()
            ->make();
    }

    return view('dashboard', compact('campaigns'));
}

dashboard.blade.php

<script type="text/javascript">
    $(document).ready(function() {
        $('#datatable').dataTable({
            responsive: true,
            processing: true,
            serverSide: true,
            ajax: '{{ route('home') }}',
            columns: [
                {data: 'DT_RowIndex', orderable: false, searchable: false},
                {data: 'subid4', name: 'subid4'},
                {data: 'sum_of_views', name: 'sum_of_views', searchable: false},
                {data: 'sum_of_term_views', name: 'sum_of_term_views', searchable: false},
                {data: 'sum_of_add_click', name: 'sum_of_add_click', searchable: false},
                {data: 'sum_of_revenue', name: 'sum_of_revenue', searchable: false},
                {data: 'rpm', name: 'rpm', searchable: false}
            ]
        });
   });
</script>

sql代码

query 1:
select  count(*) as aggregate
    from ( SELECT  `subid4`,
                   sum(view) as sum_of_views,
                   sum(term_view) as sum_of_term_views,
                   sum(add_click) as sum_of_add_click,
                   sum(revenue_usd) as sum_of_revenue,
                   (sum(revenue_usd)/sum(view)*1000) as rpm
            from  `tonic_data`
            where  `subid4` is not null
              and  `subid4` != ?
            group by  `subid4`
         ) count_row_table

query 2: 
select  `subid4`, sum(view) as sum_of_views, sum(term_view) as sum_of_term_views,
        sum(add_click) as sum_of_add_click, sum(revenue_usd) as sum_of_revenue,
            (sum(revenue_usd)/sum(view)*1000) as rpm
    from  `tonic_data`
    where  `subid4` is not null
      and  `subid4` != ?
    group by  `subid4`
    limit  10 offset 0

提前致谢!

【问题讨论】:

  • 在数据库中应用索引?你有吗?
  • 是的,我在数据库表的所有列中添加了索引。
  • 70 万行很多......为什么不使用某种搜索功能制作您的数据表,或者(我不知道 laravel)您不能对结果进行分页?很多数据表都提供了某种获取功能,无需在数据检索上等待太多。
  • @Goikiu laravel datatable 默认分页取数据。
  • 当我达到 30 万条记录时,我遇到了与 yajra 数据表相同的问题。然后我用手动 php 代码绘制了数据表。

标签: php mysql laravel performance laravel-datatables


【解决方案1】:

总结讨论,当您的查询尝试一次获取所有 700k 记录时,难怪需要很长时间。

虽然您在呈现页面时只显示有限数量的记录,但从技术上讲,查询会在页面加载时获取所有记录。

需要采取行动

  1. 最小化以下部分:

->selectRaw('sum(view) as sum_of_views, sum(term_view) as sum_of_term_views, sum(add_click) as sum_of_add_click, sum(revenue_usd) as sum_of_revenue, (sum(revenue_usd)/sum(view)*1000) as rpm')

不要即时汇总所有这些值,而是维护一个单独的表并在 tonic_data 中的任何记录发生更改时存储所有这些值。根据this answer 有回调函数。

  1. 使用 laravel 分页和排序代替 datatables,它将在服务器端执行,控制在页面加载时加载大量数据。

  2. 尝试实现基于日期或其他可能的过滤器。

【讨论】:

    【解决方案2】:

    看起来 Laravel 正在运行两次 same 查询——一次获取行数,一次获取前 10 行。

    因为GROUP BYLIMIT 10对速度影响不大。这是因为它必须收集很多行,然后进行分组,最后才交付 10 行。

    第一个查询(获取计数)可以通过更改为来简化并大大加快速度

        select  count(DISTINCT subid4) as aggregate
            from  `tonic_data`
            where  `subid4` is not null
              and  `subid4` != ?
    

    并且拥有

        INDEX(subid4)
    

    或者放弃计数(如果 Laravel 允许你这样做)。

    请提供SHOW CREATE TABLE tonic_data;我可能有更多提示。

    【讨论】:

      【解决方案3】:

      首先,我建议检查您的查询性能并尝试优化您的查询。

      其次,jquery 数据表插件有一个特性(称为管道)来缓存页面数量以减少 ajax 调用。因此,如果您已经缓存了 10 个页面,那么在第一次请求时假设它不会向服务器发出另一个请求,直到第 11 个页面被访问并继续下去。

      所以在客户端你的 ajax 将更新为

      <script type="text/javascript">
          $(document).ready(function() {
              $('#datatable').dataTable({
                  responsive: true,
                  processing: true,
                  serverSide: true,
                  ajax: $.fn.dataTable.pipeline({
                      url: '{{ route('home') }}',
                      pages: 20 // number of pages
                  })
              });
         });
      </script>
      

      更多关于流水线的帮助,请参考https://datatables.net/examples/server_side/pipeline.html

      【讨论】: