mysql分页(为什么MySQL在数据库较大的时候分页查询很慢,如何优化)
本文目录
- 为什么MySQL在数据库较大的时候分页查询很慢,如何优化
- MySQL如何优化大分页查询
- mysql 数据量大的表如何做分页查询
- mysql分页查询语句怎么写
- Mysql 数据库怎么实现分页,要说的通俗一点儿
- mysql如何做分页查询
- MySQL分页与排序语句
- Mysql分页查询
- MySQL分页的sql语言怎么写
- mysql数据库分页
为什么MySQL在数据库较大的时候分页查询很慢,如何优化
使用合理的分页方式以提高分页的效率
正如楼主所说,分页查询在我们的实际应用中非常普遍,也是最容易出问题的查询场景。比如对于下面简单的语句,一般想到的办法是在name,age,register_time字段上创建复合索引。这样条件排序都能有效的利用到索引,性能迅速提升。
如上例子,当 LIMIT 子句变成 “LIMIT 100000, 50” 时,此时我们会发现,只取50条语句为何会变慢?
原因很简单,MySQL并不知道第 100000条记录从什么地方开始,即使有索引也需要从头计算一次,因此会感觉非常的慢。
通常,我们在做分页查询时,是可以获取上一页中的某个数据标志来缩小查询范围的,比如时间,可以将上一页的最大值时间作为查询条件的一部分,SQL可以优化为这样:
若对你有所帮助,欢迎点赞、关注支持哦。
MySQL如何优化大分页查询
很多应用往往只展示最新或最热门的几条记录,但为了旧记录仍然可访问,所以就需要个分页的导航栏。然而,如何通过MySQL更好的实现分页,始终是比较令人头疼的问题。虽然没有拿来就能用的解决办法,但了解数据库的底层或多或少有助于优化分页查询。
我们先从一个常用但性能很差的查询来看一看。
SELECT *
FROM city
ORDER BY id DESC
LIMIT 0, 15
这个查询耗时0.00sec。So,这个查询有什么问题呢?实际上,这个查询语句和参数都没有问题,因为它用到了下面表的主键,而且只读取15条记录。
CREATE TABLE city (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
city varchar(128) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
真正的问题在于offset(分页偏移量)很大的时候,像下面这样:
SELECT *
FROM city
ORDER BY id DESC
LIMIT 100000, 15;
上面的查询在有2M行记录时需要0.22sec,通过EXPLAIN查看SQL的执行计划可以发现该SQL检索了100015行,但最后只需要15行。大的分页偏移量会增加使用的数据,MySQL会将大量最终不会使用的数据加载到内存中。就算我们假设大部分网站的用户只访问前几页数据,但少量的大的分页偏移量的请求也会对整个系统造成危害。Facebook意识到了这一点,但Facebook并没有为了每秒可以处理更多的请求而去优化数据库,而是将重心放在将请求响应时间的方差变小。
对于分页请求,还有一个信息也很重要,就是总共的记录数。我们可以通过下面的查询很容易的获取总的记录数。
SELECT COUNT(*)
FROM city;
然而,上面的SQL在采用InnoDB为存储引擎时需要耗费9.28sec。一个不正确的优化是采用 SQL_CALC_FOUND_ROWS,SQL_CALC_FOUND_ROWS 可以在能够在分页查询时事先准备好符合条件的记录数,随后只要执行一句 select FOUND_ROWS(); 就能获得总记录数。但是在大多数情况下,查询语句简短并不意味着性能的提高。不幸的是,这种分页查询方式在许多主流框架中都有用到,下面看看这个语句的查询性能。
SELECT SQL_CALC_FOUND_ROWS *
FROM city
ORDER BY id DESC
LIMIT 100000, 15;
这个语句耗时20.02sec,是上一个的两倍。事实证明使用 SQL_CALC_FOUND_ROWS 做分页是很糟糕的想法。
下面来看看到底如何优化。文章分为两部分,第一部分是如何获取记录的总数目,第二部分是获取真正的记录。
高效的计算行数
如果采用的引擎是MyISAM,可以直接执行COUNT(*)去获取行数即可。相似的,在堆表中也会将行数存储到表的元信息中。但如果引擎是InnoDB情况就会复杂一些,因为InnoDB不保存表的具体行数。
我们可以将行数缓存起来,然后可以通过一个守护进程定期更新或者用户的某些操作导致缓存失效时,执行下面的语句:
SELECT COUNT(*)
FROM city
USE INDEX(PRIMARY);
获取记录
下面进入这篇文章最重要的部分,获取分页要展示的记录。上面已经说过了,大的偏移量会影响性能,所以我们要重写查询语句。为了演示,我们创建一个新的表“news”,按照时事性排序(最新发布的在最前面),实现一个高性能的分页。为了简单,我们就假设最新发布的新闻的Id也是最大的。
CREATE TABLE news(
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(128) NOT NULL
) ENGINE=InnoDB;
一个比较高效的方式是基于用户展示的最后一个新闻Id。查询下一页的语句如下,需要传入当前页面展示的最后一个Id。
SELECT *
FROM news WHERE id 《 $last_id
ORDER BY id DESC
LIMIT $perpage
查询上一页的语句类似,只不过需要传入当前页的第一个Id,并且要逆序。
SELECT *
FROM news WHERE id 》 $last_id
ORDER BY id ASC
LIMIT $perpage
上面的查询方式适合实现简易的分页,即不显示具体的页数导航,只显示“上一页”和“下一页”,例如博客中页脚显示“上一页”,“下一页”的按钮。但如果要实现真正的页面导航还是很难的,下面看看另一种方式。
SELECT id
FROM (
SELECT id, ((@cnt:= @cnt + 1) + $perpage - 1) % $perpage cnt
FROM news
JOIN (SELECT @cnt:= 0)T
WHERE id 《 $last_id
ORDER BY id DESC
LIMIT $perpage * $buttons
)C
WHERE cnt = 0;
通过上面的语句可以为每一个分页的按钮计算出一个offset对应的id。这种方法还有一个好处。假设,网站上正在发布一片新的文章,那么所有文章的位置都会往后移一位,所以如果用户在发布文章时换页,那么他会看见一篇文章两次。如果固定了每个按钮的offset Id,这个问题就迎刃而解了。Mark Callaghan发表过一篇类似的博客,利用了组合索引和两个位置变量,但是基本思想是一致的。
如果表中的记录很少被删除、修改,还可以将记录对应的页码存储到表中,并在该列上创建合适的索引。采用这种方式,当新增一个记录的时候,需要执行下面的查询重新生成对应的页号。
SET p:= 0;
UPDATE news SET page=CEIL((p:= p + 1) / $perpage) ORDER BY id DESC;
当然,也可以新增一个专用于分页的表,可以用个后台程序来维护。
UPDATE pagination T
JOIN (
SELECT id, CEIL((p:= p + 1) / $perpage) page
FROM news
ORDER BY id
)C
ON C.id = T.id
SET T.page = C.page;
现在想获取任意一页的元素就很简单了:
SELECT *
FROM news A
JOIN pagination B ON A.id=B.ID
WHERE page=$offset;
还有另外一种与上种方法比较相似的方法来做分页,这种方式比较试用于数据集相对小,并且没有可用的索引的情况下—比如处理搜索结果时。在一个普通的服务器上执行下面的查询,当有2M条记录时,要耗费2sec左右。这种方式比较简单,创建一个用来存储所有Id的临时表即可(这也是最耗费性能的地方)。
CREATE TEMPORARY TABLE _tmp (KEY SORT(random))
SELECT id, FLOOR(RAND() * 0x8000000) random
FROM city;
ALTER TABLE _tmp ADD OFFSET INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, DROP INDEX SORT,ORDER BY random;
接下来就可以向下面一样执行分页查询了。
SELECT *
FROM _tmp
WHERE OFFSET 》= $offset
ORDER BY OFFSET
LIMIT $perpage;
简单来说,对于分页的优化就是。。。避免数据量大时扫描过多的记录。
mysql 数据量大的表如何做分页查询
直接用limit start, count分页语句, 也是我程序中用的方法:select * from product limit start, count当起始页较小时,查询没有性能问题,我们分别看下从10, 100, 1000, 10000开始分页的执行时间(每页取20条), 如下:select * from product limit 10, 20 0.016秒select * from product limit 100, 20 0.016秒select * from product limit 1000, 20 0.047秒select * from product limit 10000, 20 0.094秒我们已经看出随着起始记录的增加,时间也随着增大, 这说明分页语句limit跟起始页码是有很大关系的,那么我们把起始记录改为40w看下(也就是记录的一般左右) select * from product limit 400000, 20 3.229秒再看我们取最后一页记录的时间select * from product limit 866613, 20 37.44秒难怪搜索引擎抓取我们页面的时候经常会报超时,像这种分页最大的页码页显然这种时间是无法忍受的。从中我们也能总结出两件事情:1)limit语句的查询时间与起始记录的位置成正比2)mysql的limit语句是很方便,但是对记录很多的表并不适合直接使用。
mysql分页查询语句怎么写
--1.最常用的分页select * from content order by id desc limit 0, 10;--limit是MySQL中特有的分页语法,用法如下:--举例:select * from tableName limit 5; --返回前5行select * from tableName limit 0,5; --同上,返回前5行select * from tableName limit 5,10; --返回6-15行
Mysql 数据库怎么实现分页,要说的通俗一点儿
一个不带limit 一个带limit。以php+mysql为例首先,连接数据库,写一条sql语句把你要查询的信息总量查找出来sql = select count(*) from tb,$all_page ;设定每页显示条数, $display 。然后,当前页为$page ;在写一句sql = select * from tb limit $dispaly*($page - 1),$display;最后,在页面显示分页信息把当前页传回给分页处理页,一定要把相关的条件一起传回去,get 方式传值,否则查询条件改变查询信息就不正确。 网上有好多封装好的分页类。我也有一个很好用的分页类,如果请我吃肉就发给你一份哈。。\(^o^)/~ 追问: 这个$all_page用在哪儿,怎么将当前页传回给分页处理页。显示的时候那些“首页”“上一页”“下一页”“末页”是链接吗、链到什么地方,还是别的什么 回答: $all_page是查询总数,总是页数等于查询总数除以每页显示的信息。$num_page = ceil($all_page/$display); 用get方式把当前页传给分页处理页,就是《a href = "连接到本页或着不写也就是当前页?page=当前页码"》《/a》标签 别的我也想不起来,让我自己写分页,我只会最简单的那种,一般我都是调用一个现成的分页类。只需传个参数就Ok,连样式都不用写的。。。
mysql如何做分页查询
直接用limit start, count分页语句, 也是我程序中用的方法:select * from product limit start, count当起始页较小时,查询没有性能问题,我们分别看下从10, 100, 1000, 10000开始分页的执行时间(每页取20条), 如下:select * from product limit 10, 20 0.016秒select * from product limit 100, 20 0.016秒select * from product limit 1000, 20 0.047秒select * from product limit 10000, 20 0.094秒我们已经看出随着起始记录的增加,时间也随着增大, 这说明分页语句limit跟起始页码是有很大关系的,那么我们把起始记录改为40w看下(也就是记录的一般左右) select * from product limit 400000, 20 3.229秒再看我们取最后一页记录的时间select * from product limit 866613, 20 37.44秒难怪搜索引擎抓取我们页面的时候经常会报超时,像这种分页最大的页码页显然这种时间是无法忍受的。从中我们也能总结出两件事情:1)limit语句的查询时间与起始记录的位置成正比2)mysql的limit语句是很方便,但是对记录很多的表并不适合直接使用。
MySQL分页与排序语句
MySQLLIMIT接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是0(而不是1)下面是一个使用LIMIT查询的例子:SELECTSALE_DATE,SUM(SALE_MONEY)ASSUM_MONEYFROMSALE_REPORTGROUPBYSALE_DATEORDERBYSUM(SALE_MONEY)DESCLIMIT0,10
Mysql分页查询
limit "(currentPage -1 ) * pageSize",pageSize;--》limit x,y;//x表示从数据库查询的临时表的第x行开始,pageSize表示获得几行,即每页的数据条数;x=(currentPage -1 ) * pageSize;y=pageSize;pageSize就是lineSize,就是分页后每页显示多少行记录。
MySQL分页的sql语言怎么写
1、首先我们建立一个表表的数据,这个表里有25条数据,id从1到25。(下图是部分截图)
2、要分页数据,首先我们假设一页有10条数据,我们可以用mysql的 limit关键字来限定返回多少条数据。并且用order by来排序数据,这里用 id来排序。所以第一页的sql可以如图这样写。
3、执行后得到的数据如图,就是 id从1到10的前10条数据,因为我们是按id升序来排序的。
4、上面第一页的sql是简化的写法,完整的写法如图,得到的结果和上图的一模一样。代码里 limit 0, 10 的意思是从第一条数据开始,取10条数据。(注意的是第一条数据是从0开始的)
5、那么第二页的数据,关键是要知道是从哪一条数据开始,可以用这个公式得到: (页码-1) * 每页显示多少条,即 (2-1) * 10 = 10, 所以sql语句如图, limit 10, 10。
6、执行后,结果正确,得到id从11到20的10条数据。
7、同理第三页数据的sql如图,《br/》就是 limit 20, 10。
8、查询的结果如图,因为这页只剩下5条数据了,所以只显示5条数据。如果你有更多页的数据,后面的数据只需要按上面的公式,得到从哪行开始,就可以写对应的sql语句了。
mysql数据库分页
很多应用往往只展示最新或最热门的几条记录,但为了旧记录仍然可访问,所以就需要个分页的导航栏。然而,如何通过MySQL更好的实现分页,始终是比较令人头疼的问题。虽然没有拿来就能用的解决办法,但了解数据库的底层或多或少有助于优化分页查询。我们先从一个常用但性能很差的查询来看一看。SELECT *FROM cityORDER BY id DESCLIMIT 0, 15这个查询耗时0.00sec。So,这个查询有什么问题呢?实际上,这个查询语句和参数都没有问题,因为它用到了下面表的主键,而且只读取15条记录。CREATE TABLE city ( id int(10) unsigned NOT NULL AUTO_INCREMENT, city varchar(128) NOT NULL, PRIMARY KEY (id)) ENGINE=InnoDB;真正的问题在于offset(分页偏移量)很大的时候,像下面这样:SELECT *FROM cityORDER BY id DESCLIMIT 100000, 15;上面的查询在有2M行记录时需要0.22sec,通过EXPLAIN查看SQL的执行计划可以发现该SQL检索了100015行,但最后只需要15行。大的分页偏移量会增加使用的数据,MySQL会将大量最终不会使用的数据加载到内存中。就算我们假设大部分网站的用户只访问前几页数据,但少量的大的分页偏移量的请求也会对整个系统造成危害。Facebook意识到了这一点,但Facebook并没有为了每秒可以处理更多的请求而去优化数据库,而是将重心放在将请求响应时间的方差变小。对于分页请求,还有一个信息也很重要,就是总共的记录数。我们可以通过下面的查询很容易的获取总的记录数。SELECT COUNT(*)FROM city;然而,上面的SQL在采用InnoDB为存储引擎时需要耗费9.28sec。一个不正确的优化是采用 SQL_CALC_FOUND_ROWS,SQL_CALC_FOUND_ROWS 可以在能够在分页查询时事先准备好符合条件的记录数,随后只要执行一句 select FOUND_ROWS(); 就能获得总记录数。但是在大多数情况下,查询语句简短并不意味着性能的提高。不幸的是,这种分页查询方式在许多主流框架中都有用到,下面看看这个语句的查询性能。SELECT SQL_CALC_FOUND_ROWS *FROM cityORDER BY id DESCLIMIT 100000, 15;这个语句耗时20.02sec,是上一个的两倍。事实证明使用 SQL_CALC_FOUND_ROWS 做分页是很糟糕的想法。下面来看看到底如何优化。文章分为两部分,第一部分是如何获取记录的总数目,第二部分是获取真正的记录。高效的计算行数如果采用的引擎是MyISAM,可以直接执行COUNT(*)去获取行数即可。相似的,在堆表中也会将行数存储到表的元信息中。但如果引擎是InnoDB情况就会复杂一些,因为InnoDB不保存表的具体行数。我们可以将行数缓存起来,然后可以通过一个守护进程定期更新或者用户的某些操作导致缓存失效时,执行下面的语句:SELECT COUNT(*)FROM cityUSE INDEX(PRIMARY);获取记录下面进入这篇文章最重要的部分,获取分页要展示的记录。上面已经说过了,大的偏移量会影响性能,所以我们要重写查询语句。为了演示,我们创建一个新的表“news”,按照时事性排序(最新发布的在最前面),实现一个高性能的分页。为了简单,我们就假设最新发布的新闻的Id也是最大的。CREATE TABLE news( id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, title VARCHAR(128) NOT NULL) ENGINE=InnoDB;一个比较高效的方式是基于用户展示的最后一个新闻Id。查询下一页的语句如下,需要传入当前页面展示的最后一个Id。SELECT *FROM news WHERE id 《 $last_idORDER BY id DESCLIMIT $perpage查询上一页的语句类似,只不过需要传入当前页的第一个Id,并且要逆序。SELECT *FROM news WHERE id 》 $last_idORDER BY id ASCLIMIT $perpage上面的查询方式适合实现简易的分页,即不显示具体的页数导航,只显示“上一页”和“下一页”,例如博客中页脚显示“上一页”,“下一页”的按钮。但如果要实现真正的页面导航还是很难的,下面看看另一种方式。SELECT idFROM ( SELECT id, ((@cnt:= @cnt + 1) + $perpage - 1) % $perpage cnt FROM news JOIN (SELECT @cnt:= 0)T WHERE id 《 $last_id ORDER BY id DESC LIMIT $perpage * $buttons)CWHERE cnt = 0;通过上面的语句可以为每一个分页的按钮计算出一个offset对应的id。这种方法还有一个好处。假设,网站上正在发布一片新的文章,那么所有文章的位置都会往后移一位,所以如果用户在发布文章时换页,那么他会看见一篇文章两次。如果固定了每个按钮的offset Id,这个问题就迎刃而解了。Mark Callaghan发表过一篇类似的博客,利用了组合索引和两个位置变量,但是基本思想是一致的。如果表中的记录很少被删除、修改,还可以将记录对应的页码存储到表中,并在该列上创建合适的索引。采用这种方式,当新增一个记录的时候,需要执行下面的查询重新生成对应的页号。SET p:= 0;UPDATE news SET page=CEIL((p:= p + 1) / $perpage) ORDER BY id DESC;当然,也可以新增一个专用于分页的表,可以用个后台程序来维护。UPDATE pagination TJOIN ( SELECT id, CEIL((p:= p + 1) / $perpage) page FROM news ORDER BY id)CON C.id = T.idSET T.page = C.page;现在想获取任意一页的元素就很简单了:SELECT *FROM news AJOIN pagination B ON A.id=B.IDWHERE page=$offset;还有另外一种与上种方法比较相似的方法来做分页,这种方式比较试用于数据集相对小,并且没有可用的索引的情况下—比如处理搜索结果时。在一个普通的服务器上执行下面的查询,当有2M条记录时,要耗费2sec左右。这种方式比较简单,创建一个用来存储所有Id的临时表即可(这也是最耗费性能的地方)。CREATE TEMPORARY TABLE _tmp (KEY SORT(random))SELECT id, FLOOR(RAND() * 0x8000000) randomFROM city; ALTER TABLE _tmp ADD OFFSET INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, DROP INDEX SORT,ORDER BY random;接下来就可以向下面一样执行分页查询了。SELECT *FROM _tmpWHERE OFFSET 》= $offsetORDER BY OFFSETLIMIT $perpage;简单来说,对于分页的优化就是。。。避免数据量大时扫描过多的记录。
更多文章:
x240笔记本有通病么(X240新买的,怎么发热那么厉害,烫手而且在右侧部分,里边偶尔会有吱~的一声响,硬盘有问题吧)
2024年7月24日 13:11
联想智能手机广告(联想智能手机广告中的广告词:那些年,我们一起追的梦 后面是什么)
2024年7月9日 02:28
thinkpad t410i(联想ThinkPad E40和ThinkPad T410i哪个好)
2024年3月23日 11:40
戴尔xps13什么时候出的(戴尔xps13 7390什么时候出的)
2024年5月1日 18:10
华硕f8s笔记本升级(华硕笔记本F8S显卡9300坏了能换新的不)
2024年7月16日 01:05
华硕u盘重装win10系统步骤(怎样用u盘装win10系统)
2024年1月19日 10:55
郑州电脑维修培训(郑州电脑维修去哪里好,芯片级的维修,在杭州学习,也行,谢谢)
2024年5月31日 17:03
gateway笔记本u盘启动(gateway笔记本 BOSS从U盘启动设置)
2024年2月4日 13:40
华为最新款手机图片(华为nova7和nova7pro有什么差别,两款手机买哪个更好)
2024年5月19日 20:02
funhouse笔记本电脑官网(hunhouse笔记本电脑可以吗)
2024年4月2日 17:40
戴尔手机多少钱(戴尔Venue Pro这款手机怎样,价格大概多少)
2023年7月3日 13:00
联想和thinkpad哪个质量好(lenovo和thinkpad哪个好)
2024年7月4日 15:50