数据量大的情况下 Laravel 获取分页查询时很慢,有什么优化方案?

数据量大的情况下 Laravel 获取分页查询时很慢,有什么优化方案?

Laravel

上面第一条 SQL 是 laravel 分页时用来获取数据总数的,第二条是获取当前页数据的。
第一条竟然要三百多毫秒,第二条1毫秒都不用。users 数据表有200万行数据,这种情况有什么解决方案没,大家是如何优化的

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
最佳答案

增加一个搜索项:创建日期,默认是最近几天,然后创建时间加索引

8个月前 评论
h-o-o (楼主) 7个月前
xiaogui 7个月前
讨论数量: 24

count慢导致的,自己写不用它的

8个月前 评论
loading-ss 8个月前
h-o-o (楼主) 8个月前
thus (作者) 8个月前

直接不获取数据总数,不做页码就好了,只做上一页下一页

8个月前 评论
h-o-o (楼主) 8个月前
wanghan 8个月前

数据量大时候的limit也有性能问题

8个月前 评论
h-o-o (楼主) 8个月前
pigzzz (作者) 8个月前
h-o-o (楼主) 8个月前
Lichmaker 8个月前
h-o-o (楼主) 8个月前

覆盖索引可否?

8个月前 评论
h-o-o (楼主) 8个月前
NeoC (作者) 8个月前
h-o-o (楼主) 8个月前
NeoC (作者) 8个月前
NeoC (作者) 8个月前

数据量多大?

8个月前 评论
h-o-o (楼主) 8个月前
Epona

可以试试。select count(id)*的话肯定慢的。

8个月前 评论
h-o-o (楼主) 8个月前
anniversary 8个月前
chnyang 4个月前
vinhson

300毫秒有什么大惊小怪的,那你是没见过我们公司十几秒的接口

8个月前 评论
Artist0618 8个月前
h-o-o (楼主) 8个月前
vinhson (作者) 8个月前

mysql,又是简单的select的话 可以试试 explain select * from user?

\DB::select('explain select id from user;');

返回值有个rows

8个月前 评论
wanghan 8个月前
Lichmaker 8个月前

@izudo 这个查询还有误差的吗?

    $explain = DB::select('explain select id from users');
    $count = DB::select('select count(*) as aggregate from `users`');
    return $explain[0]->rows . ' - ' . $count[0]->aggregate; // 输出 1959223 - 1999997
8个月前 评论

@izudo
还有个问题就是,无论 'explain select' 查询多少行,无论是1行还是200万行,其执行时间基本都不变。

file

file

8个月前 评论

@h-o-o 好像有误差,这也是我网上搜的方法,explain 本来只是mysql用来分析sql语句执行情况用的。
还有一个方法就是 通过mysql 的information_schema表来查询

\DB::select('select * from information_schema.`TABLES` as t where t.TABLE_NAME=\'user\';');
8个月前 评论
h-o-o (楼主) 8个月前
izudo (作者) 8个月前
h-o-o (楼主) 8个月前

200W就叫海量了?

8个月前 评论

这个应该是第一次连接耗时也含进去了吧,你在执行分页之前再执行一个查询看看时间 :joy:

8个月前 评论
h-o-o (楼主) 8个月前

@h-o-o

在model里写个方法

public function getRowCount()
{
    $database = env('DB_DATABASE', config('database.connections.mysql.database'));
    $r = \DB::select("select * from information_schema.`TABLES` as t where t.TABLE_NAME='{$this->table}' and TABLE_SCHEMA='{$database}';");
    return $r[0]->TABLE_ROWS ?? 0;
}

仅用于mysql,还得有information_schema这个表的访问权限
有表前缀的话,把方法里的表名那处理下就好了

8个月前 评论
h-o-o (楼主) 8个月前
izudo (作者) 8个月前
h-o-o (楼主) 8个月前

200w数据,一定要全部展示出来吗,我想没有人会闲到一页一页点,点到200w吧,只展示部分数据,其他的通过条件过滤再查询出来,这样不是挺好吗,我这边系统里,SaaS后台订单列表1000多万,只展示最新的50w

8个月前 评论

评论了就别删除啊?

file
戾气这么重?

8个月前 评论
h-o-o (楼主) 8个月前

@MayTsing 我也觉得是第一次连接耗时也算进去了

8个月前 评论

说实在的,200万的数据真的不是很大。mysql的话千万级别的数据也勉强可以顶住。有没有考虑过服务器的原因(自己猜测而已)

8个月前 评论
h-o-o (楼主) 8个月前
h-o-o (楼主) 8个月前

InnoDB 引擎的话,从SQL 层面没法优化!那些说 count(id) 的属于误导。

在 InnoDB 存储引擎中,primary key 是 clustered index(最大的索引),使用它来处理 select count(*) from table_name 最慢。

你可以考虑对总数进行缓存,插入和删除维护一下缓存的总数就行了。或者使用数据表统计的近似值。
然后在控制器里 使用 return new \Illuminate\Pagination\LengthAwarePaginator($data, $total, 15, 1);

8个月前 评论

分页自己写,select * from users where id > last_id limit 10 理论上没毛病

8个月前 评论
h-o-o (楼主) 8个月前
巨大的易拉罐

我个人认为,百万的数据其实也不算小,sql在怎么优化,分页count都会耗时的,建议跟楼上的一样,第一次先查出来总数,缓存出来,insert和del时,对总数修改就可以..

8个月前 评论

增加一个搜索项:创建日期,默认是最近几天,然后创建时间加索引

8个月前 评论
h-o-o (楼主) 7个月前
xiaogui 7个月前
Code_Er

如果一定需要count总数的话 我觉得课可以用异步处理好存放在redis上

7个月前 评论
h-o-o (楼主) 7个月前
Code_Er (作者) 7个月前

@h-o-o 方便问一下,图中的QUERY时间怎么查看,或者要用甚么工具,谢谢

7个月前 评论
h-o-o (楼主) 5个月前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!