当前位置: 首页 > 科技观察

MySQL大数据量分页查询方法及其优化

时间:2023-03-17 00:43:24 科技观察

方法一:直接使用数据库提供的SQL语句语句风格:MySQL中可以使用如下方法:SELECT*FROMtablenameLIMITM,N适用场景:适用于数据量比较大的情况(几百/千元组)理由/缺点:全表扫描,速度会很慢,有些数据库结果集返回不稳定(比如一次性返回1,2,3,和2,1再一次,3)。limit是从结果集的M位置取N个输出,其余丢弃。方法二:创建主键或唯一索引,并使用索引(假设每页10条)语句形式:在MySQL中,可以使用如下方法:SELECT*FROMtablenameWHEREid_pk>(pageNum*10)LIMITM适配场景:适用于数据量大(几万个元组)的情况原因:索引扫描,速度会很快。有朋友建议:由于数据查询不是按照pk_id排序,所以会出现数据丢失的情况,只有方法三方法三:根据索引重新排序语句风格:在MySQL中,可以使用如下方法:SELECT*FROMtablenameWHEREid_pk>(pageNum*10)ORDERBYid_pkASCLIMITM适用场景:适用于数据量较大(上万元组)的情况。***ORDERBY后面的列对象是主键或者唯一的,这样可以使用索引淘汰ORDERBY操作但是结果集合是稳定的(稳定的意思见方法一)原因:索引扫描,速度会很快。但是MySQL的排序操作只有ASC没有DESC(DESC是假的,以后会做真正的DESC,期待。。。)。方法四:根据索引,prepare中第一个问号表示pageNum,第二个?表示每页的元组数语句风格:在MySQL中,可以采用以下方式:PREPAREstmt_nameFROMSELECT*FROMtablenameWHEREid_pk>(?*?)ORDERBYid_pkASCLIMITM适配场景:数据量大原因:索引扫描,速度会很快。prepare语句比一般查询语句快一点。方法五:使用MySQL支持ORDER操作。可以使用索引快速定位到一些元组,避免全表扫描。例如:读取1000行到1019行的元组(pk为主键/唯一键)。SELECT*FROMyour_tableWHEREpk>=1000ORDERBYpkASCLIMIT0,20方法六:使用“subquery/join+index”快速定位元组的位置,然后读取元组。例如(id是主键/唯一键,蓝色字体的变量)使用子查询示例:SELECT*FROMyour_tableWHEREid<=>(SELECTidFROMyour_tableORDERBYiddescLIMIT($page-1)*$pagesizeORDERBYiddescLIMIT$pagesizeUsing连接示例:SELECT*FROMyour_tableAST1JOIN(SELECTidFROMyour_tableORDERBYiddescLIMIT($page-1)*$pagesizeASt2WHEREt1.id<=>Mysql大数据页数使用limit,查询效率越低测试实验1.直接使用limitstart,count分页语句,也是我程序中使用的方法:select*fromproductlimitstart,count起始页较小时,查询没有性能问题,我们看一下从10、100、1000、10000开始分页的执行时间(每页取20条)。如下:select*fromproductlimit10,200.016secondsselect*fromproductlimit100,200.016secondsselect*fromproductlimit1000,200.047secondsselect*fromproductlimit10000,200.094seconds我们看到随着初始记录的增加,时间也随之增加,可见分页语句limit跟起始页码有很大关系,所以我们把起始记录改成40w看一下(也就是关于记录)select*fromproductlimit400000,203.229秒然后看我们fetch*的时间**pagerecordselect*fromproductlimit866613,2037.44seconds分页的页码page***显然是受不了了。由此我们也可以得出两点:limit语句的查询时间与初始记录的位置成正比。mysql的limit语句很方便,但是对于记录很多的表不适合直接使用。2.极限分页问题的性能优化方法使用表的覆盖索引来加速分页查询。我们都知道,如果在使用索引查询的语句中只包含索引列(覆盖索引),那么这种情况下查询会非常快。.因为有索引搜索的优化算法,数据在查询索引上,不需要再去寻找相关的数据地址,节省了很多时间。另外Mysql中有相关的索引缓存,在并发高的时候使用缓存效果更好。在我们的例子中,我们知道id字段是主键,其中自然包括默认的主键索引。现在让我们看看使用覆盖索引的查询执行得如何。这次我们查询最后一页的数据(使用覆盖索引,只包括id列),如下:selectidfromproductlimit866613,200.2秒比查询所有列时的37.44秒快100倍左右,那么如果我们还想查询所有列。有两种方法,一种是id>=的形式,一种是使用join看实际情况:SELECT*FROMproductWHEREID>=(selectidfromproductlimit866613,1)limit20查询时间为0.2秒!另一种写法SELECT*FROMproductaJOIN(selectidfromproductlimit866613,20)bONa.ID=b.id查询时间也很短!3、复合索引优化方法MySql的性能有多高?MySql数据库绝对适合DBA级别的高手来玩。一般10000条消息随便写一个小系统都可以,使用xx框架可以实现快速开发。但是当数据量达到十万、几百万、几千万的时候,它的性能还能这么高吗?一个小错误就可能导致整个系统的重写,甚至导致系统无法正常运行!好了,废话不多说了。用事实说话,看个例子:数据表collect(id,title,info,vtype)有这4个字段,其中title使用定长,info使用text,id为gradual,vtype为tinyint,vtype为index。这是一个基本新闻系统的简单模型。现在用数据填充它,用100,000篇新闻文章填充它。***collect为10万条记录,数据库表占用硬盘1.6G。OK,看下面的sql语句:selectid,titlefromcollectlimit1000,10;很快;基本上0.01秒就OK了,再看下面selectid,titlefromcollectlimit90000,10;从90,000开始分页,结果呢?8-9秒完成,我的天哪?其实要优化这个数据,网上就能找到答案。看下面的语句:selectidfromcollectorderbyidlimit90000,10;很快,0.04秒就OK了。为什么?因为用id主键做索引当然快。在线修改方法为:selectid,titlefromcollectwhereid>=(selectidfromcollectorderbyidlimit90000,1)limit10;这是使用id作为索引的结果。但是如果问题再复杂一点,就完了。看下面的语句selectidfromcollectwherevtype=1orderbyidlimit90000,10;很慢,花了8-9秒!到了这里,相信很多人都会有和我一样的崩溃感!vtype是否被索引?怎么会慢呢?有vtype索引很好。你直接selectidfromcollectwherevtype=1limit1000,10;速度很快,基本上是0.05秒,但是快了90倍。从9万开始,就是0.05*90=4.5秒。并且测试结果是8-9秒一个数量级。从这里开始,有人提出了分表的想法,和dis#cuz论坛的想法是一样的。思路是这样的:建立一个索引表:t(id,title,vtype)并设置为固定长度,然后做分页,将结果分页出来,然后再去收集查找信息。可行吗?试验过后就知道了。t(id,title,vtype)中存储了10万条记录,数据表大小约20M。使用selectidfromtwherevtype=1orderbyidlimit90000,10;很快。基本上,它可以在0.1-0.2秒内运行。为什么会这样?我猜是因为收集的数据太多,所以分页要跑很远的路。limit完全和数据表的大小有关。其实这还是全表扫描,只是因为数据量小,只有10万条很快。OK,来个疯狂的实验,加起来100万,测试一下性能。加10倍数据后,t表立马达到200M多,而且是定长。还是刚才的查询语句,时间0.1-0.2秒完成!分表性能有问题吗?错误的!因为我们的上限还是9万,这么快。给个大的,从900000开始selectidfromtwherevtype=1orderbyidlimit900000,10;看结果,时间是1-2秒!为什么?细分之后时间还是那么长,很郁闷!有人说固定长度会提高极限的性能。一开始我还以为,因为一条记录的长度是固定的,所以mysql应该可以算出90万的位置吧?但是我们高估了mysql的智能。它不是商业数据库。事实证明定长和不定长对limit影响不大?难怪有人说discuz到了100万条记录就会很慢。我相信这是真的,而且与数据库设计有关!MySQL不能突破100万的限制吗???达到100万页就真的达到极限了吗?答案是不。为什么不能突破100万是因为我不会设计mysql。下面介绍非分表的方法,来疯狂测试一下吧!一张表100万条记录,10G数据库,如何快速分页!嗯,我们测试回collect表,测试结论是:30万条数据,分表的方式是可行的,超过30万条速度会变慢,你受不了!当然,如果你用分表+我的方式,那绝对是完美的。但是用了我的方法之后,不用分表就可以安全解决了!答案是:复合指数!有一次在设计mysql索引的时候,无意中发现索引的名??字可以任意选择,可以选择几个字段来输入。这有什么用?初始selectidfromcollectororderbyidlimit90000,10;之所以这么快,是因为取了索引,但是如果加上where,索引就不会取了。抱着试一试的想法,我加了一个类似search(vtype,id)的索引。然后测试selectidfromcollectwherevtype=1limit90000,10;非常快!0.04秒完成!再次测试:selectid,titlefromcollectwherevtype=1limit90000,10;非常抱歉,8-9秒,没有搜索索引!再测试:search(id,vtype),还是语句selectid,也是很遗憾,0.5秒。总结一下:如果有where条件,要使用limit索引,就必须设计一个索引,把where放在第一位,把limit使用的主键放在第二位,只能选择主键!***解决了分页问题。如果能快速返回id,希望可以优化限制。按照这个逻辑,最大限制应该在0.0x秒内划分。看来mysql语句的优化和索引很重要啊!原文链接:https://www.cnblogs.com/geningchao/p/6649907.html