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

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

Laravel

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

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
最佳答案

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

4年前 评论
h-o-o (楼主) 4年前
91it 4年前
讨论数量: 24

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

4年前 评论
loading-ss 4年前
h-o-o (楼主) 4年前
rufo (作者) 4年前

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

4年前 评论
h-o-o (楼主) 4年前
wanghan 4年前

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

4年前 评论
h-o-o (楼主) 4年前
pigzzz (作者) 4年前
h-o-o (楼主) 4年前
Lichmaker 4年前
h-o-o (楼主) 4年前

覆盖索引可否?

4年前 评论
h-o-o (楼主) 4年前
NeoC (作者) 4年前
h-o-o (楼主) 4年前
NeoC (作者) 4年前
NeoC (作者) 4年前

数据量多大?

4年前 评论
h-o-o (楼主) 4年前
Epona

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

4年前 评论
h-o-o (楼主) 4年前
anniversary 4年前
chnyang 3年前

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

4年前 评论
Artist0618 4年前
h-o-o (楼主) 4年前
vinhson (作者) 4年前

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

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

返回值有个rows

4年前 评论
wanghan 4年前
Lichmaker 4年前

@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
4年前 评论

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

file

file

4年前 评论

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

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

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

4年前 评论
h-o-o (楼主) 4年前

@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这个表的访问权限
有表前缀的话,把方法里的表名那处理下就好了

4年前 评论
h-o-o (楼主) 4年前
izudo (作者) 4年前
h-o-o (楼主) 4年前

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

4年前 评论

评论了就别删除啊?

file
戾气这么重?

4年前 评论
h-o-o (楼主) 4年前

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

4年前 评论

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

4年前 评论
h-o-o (楼主) 4年前
h-o-o (楼主) 4年前

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

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

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

4年前 评论

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

4年前 评论
h-o-o (楼主) 4年前
巨大的易拉罐

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

4年前 评论

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

4年前 评论
h-o-o (楼主) 4年前
91it 4年前
Code_Er

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

4年前 评论
h-o-o (楼主) 4年前
Code_Er (作者) 4年前

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

4年前 评论
h-o-o (楼主) 4年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!