分类: MySQL2011-10-31 13:53 1277人阅读 评论(0) 收藏 举报

MySQL5.1中子查询是不能使用LIMIT的,报错: "This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' "

这样的语句是不能正确执行的。
select * from message where id in (select id from message order by id desc limit 10);

但是,只要你再来一层就行。如:
select * from message where id in(select m.id from (select * from message order by id desc limit 10) as m) order by id asc

这样就可以绕开limit子查询的问题。

mysql limit 大数据量分页优化步骤

mysql limit 大数据量分页优化方法

Mysql的优化是非常重要的。其他最常用也最需要优化的就是limit。Mysql的limit给分页带来了极大的方便,但数据量一大的时候,limit的性能就急剧下降。

  同样是取10条数据

  select * from yanxue8_visit limit 10000,10 和

  select * from yanxue8_visit limit 0,10
  就不是一个数量级别的。

  网上也很多关于limit的五条优化准则,都是翻译自Mysql手册,虽然正确但不实用。今天发现一篇文章写了些关于limit优化的,很不错。

  文中不是直接使用limit,而是首先获取到offset的id然后直接使用limit size来获取数据。根据他的数据,明显要好于直接使用limit。这里我具体使用数据分两种情况进行测试。(测试环境win2033+p4双核 (3GHZ) +4G内存 Mysql 5.0.19)

  1、offset比较小的时候。

  select * from yanxue8_visit limit 10,10

  多次运行,时间保持在0.0004-0.0005之间http://www.zhutiai.com

  Select * From yanxue8_visit Where vid >=(

  Select vid From yanxue8_visit Order By vid limit 10,1

  ) limit 10
  多次运行,时间保持在0.0005-0.0006之间,主要是0.0006

  结论:偏移offset较小的时候,直接使用limit较优。这个显然是子查询的原因。

  2、offset大的时候。

 

 select * from yanxue8_visit limit 10000,10

  多次运行,时间保持在0.0187左右

  Select * From yanxue8_visit Where vid >=(

  Select vid From yanxue8_visit Order By vid limit 10000,1

  ) limit
10

  多次运行,时间保持在0.0061左右,只有前者的1/3。可以预计offset越大,后者越优。

  以后要注意改正自己的limit语句,优化一下Mysql了

据表 collect ( id, title ,info ,vtype) 就这4个字段,其中 title 用定长,info 用text, id 
是逐渐,vtype是tinyint,vtype是索引。这是一个基本的新闻系统的简单模型。现在往里面填充数据,填充10万篇新闻。

最后collect 为 10万条记录,数据库教程表占用硬盘1.6G。OK ,看下面这条sql语句:

select id,title from collect limit 1000,10; 很快;基本上0.01秒就OK,再看下面的

select id,title from collect limit 90000,10; 从9万条开始分页,结果?
8-9秒完成,my god 哪出问题了????其实要优化这条数据,网上找得到答案。看下面一条语句:

select id from collect order by id limit 90000,10; 很快,0.04秒就OK。 
为什么?因为用了id主键做索引当然快。网上的改法是:

select id,title from collect where id>=(select id from collect order by id 
limit 90000,1) limit 10;
这就是用了id做索引的结果。可是问题复杂那么一点点,就完了。看下面的语句

select id from collect where vtype=1 order by id limit 90000,10; 
很慢,用了8-9秒!

到了这里我相信很多人会和我一样,有崩溃感觉!vtype 做了索引了啊?怎么会慢呢?vtype做了索引是不错,你直接 select id from 
collect where vtype=1 limit 1000,10; 
是很快的,基本上0.05秒,可是提高90倍,从9万开始,那就是0.05*90=4.5秒的速度了。和测试结果8-9秒到了一个数量级。从这里开始有人提出了分表的思路,这个和discuz 
论坛是一样的思路。思路如下:

建一个索引表: t (id,title,vtype) 并设置成定长,然后做分页,分页出结果再到 collect 里面去找info 。 
是否可行呢?实验下就知道了。

10万条记录到 t(id,title,vtype) 里,数据表大小20M左右。用

select id from t where vtype=1 order by id limit 90000,10; 
很快了。基本上0.1-0.2秒可以跑完。为什么会这样呢?我猜想是因为collect 数据太多,所以分页要跑很长的路。limit 
完全和数据表的大小有关的。其实这样做还是全表扫描,只是因为数据量小,只有10万才快。OK, 来个疯狂的实验,加到100万条,测试性能。

加了10倍的数据,马上t表就到了200多M,而且是定长。还是刚才的查询语句,时间是0.1-0.2秒完成!分表性能没问题?错!因为我们的limit还是9万,所以快。给个大的,90万开始

select id from t where vtype=1 order by id limit 900000,10; 看看结果,时间是1-2秒!
why ?? 分表了时间还是这么长,非常之郁闷!有人说定长会提高limit的性能,开始我也以为,因为一条记录的长度是固定的,mysql教程 
应该可以算出90万的位置才对啊? 可是我们高估了mysql 的智能,他不是商务数据库,事实证明定长和非定长对limit影响不大? 怪不得有人说 
discuz到了100万条记录就会很慢,我相信这是真的,这个和数据库设计有关!

难道MySQL 无法突破100万的限制吗???到了100万的分页就真的到了极限???

答案是: NO !!!! 
为什么突破不了100万是因为不会设计mysql造成的。下面介绍非分表法,来个疯狂的测试!一张表搞定100万记录,并且10G 
数据库,如何快速分页!

好了,我们的测试又回到 collect表,开始测试结论是: 
30万数据,用分表法可行,超过30万他的速度会慢道你无法忍受!当然如果用分表+我这种方法,那是绝对完美的。但是用了我这种方法后,不用分表也可以完美解决!

答案就是:复合索引! 有一次设计mysql索引的时候,无意中发现索引名字可以任取,可以选择几个字段进来,这有什么用呢?开始的select id from 
collect order by id limit 90000,10; 这么快就是因为走了索引,可是如果加了where 就不走索引了。抱着试试看的想法加了 
search(vtype,id) 这样的索引。然后测试
select id from collect where vtype=1 limit 90000,10; 非常快!0.04秒完成!

再测试: select id ,title from collect where vtype=1 limit 90000,10; 
非常遗憾,8-9秒,没走search索引!

再测试:search(id,vtype),还是select id 这个语句,也非常遗憾,0.5秒。
综上:如果对于有where 条件,又想走索引用limit的,必须设计一个索引,将where 
放第一位,limit用到的主键放第2位,而且只能select 主键!

完美解决了分页问题了。可以快速返回id就有希望优化limit , 按这样的逻辑,百万级的limit 应该在0.0x秒就可以分完。看来mysql 
语句的优化和索引时非常重要的!
好了,回到原题,如何将上面的研究成功快速应用于开发呢?如果用复合查询,我的轻量级框架就没的用了。分页字符串还得自己写,那多麻烦?这里再看一个例子,思路就出来了:
select * from collect where id in (9000,12,50,7000); 竟然 0秒就可以查完!
mygod ,mysql 的索引竟然对于in语句同样有效!看来网上说in无法用索引是错误的!
有了这个结论,就可以很简单的应用于轻量级框架了:
代码如下:

复制代码 代码如下:

$db=dblink(); 
$db->pagesize=20; 
$sql="select id from collect where vtype=$vtype"; 
$db->execute($sql); 
$strpage=$db->strpage(); 
//将分页字符串保存在临时变量,方便输出 
while($rs=$db->fetch_array()){ 
$strid.=$rs['id'].','; 

$strid=substr($strid,0,strlen($strid)-1); 
//构造出id字符串 
$db->pagesize=0; 
//很关键,在不注销类的情况下,将分页清空,这样只需要用一次数据库连接,不需要再开; 
$db->execute("select 
id,title,url,sTime,gTime,vtype,tag from collect where id in ($strid)"); 
<?php教程 while($rs=$db->fetch_array()): ?> 
<tr> 
<td> <?php echo $rs['id'];?></td> 
<td> <?php echo $rs['url'];?></td> 
<td> <?php echo $rs['sTime'];?></td> 
<td> <?php echo $rs['gTime'];?></td> 
<td> <?php echo $rs['vtype'];?></td> 
<td> <a href="?act=show&id=<?php echo $rs['id'];?>" 
target="_blank"><?php echo $rs['title'];?></a></td> 
<td> <?php echo 
$rs['tag'];?></td> 
</tr> 
<?php endwhile; 
?> 
</table> 
<?php 
echo $strpage; 
?> 通过简单的变换,其实思路很简单:1)通过优化索引,找出id,并拼成 "123,90000,12000" 这样的字符串。2)第2次查询找出结果。 
小小的索引+一点点的改动就使mysql 可以支持百万甚至千万级的高效分页! 
通过这里的例子,我反思了一点:对于大型系统,PHP千万不能用框架,尤其是那种连sql语句都看不到的框架!因为开始对于我的轻量级框架都差点崩溃!只适合小型应用的快速开发,对于ERP,OA,大型网站,数据层包括逻辑层的东西都不能用框架。如果程序员失去了对sql语句的把控,那项目的风险将会成几何级数增加!尤其是用mysql 
的时候,mysql 一定需要专业的dba 才可以发挥他的最佳性能。一个索引所造成的性能差别可能是上千倍!

性能优化:
基于MySQL5.0中limit的高性能,我对数据分页也重新有了新的认识.

1.
Select * From cyclopedia Where ID>=(
Select Max(ID) From (
 Select ID From cyclopedia Order By ID limit 90001
) As tmp
) limit 100;

2.
Select * From cyclopedia Where ID>=(
Select Max(ID) From (
 Select ID From cyclopedia Order By ID limit 90000,1
) As tmp
) limit 100;
同样是取90000条后100条记录,第1句快还是第2句快?
第1句是先取了前90001条记录,取其中最大一个ID值作为起始标识,然后利用它可以快速定位下100条记录
第2句择是仅仅取90000条记录后1条,然后取ID值作起始标识定位下100条记录
第1句执行结果.100 rows in set (0.23) sec
第2句执行结果.100 rows in set (0.19) sec

很明显第2句胜出.看来limit好像并不完全像我之前想象的那样做全表扫描返回limit offset+length条记录,这样看来limit比起MS-SQL的Top性能还是要提高不少的.

其实第2句完全可以简化成

Select * From cyclopedia Where ID>=(
Select ID From cyclopedia limit 90000,1
)limit 100;
直接利用第90000条记录的ID,不用经过Max运算,这样做理论上效率因该高一些,但在实际使用中几乎看不到效果,因为本身定位ID返回的就是1条记录,Max几乎不用运作就能得到结果,但这样写更清淅明朗,省去了画蛇那一足.

可是,既然MySQL有limit可以直接控制取出记录的位置,为什么不干脆用Select * From cyclopedia limit 90000,1呢?岂不更简洁?
这样想就错了,试了就知道,结果是:1 row in set (8.88) sec,怎么样,够吓人的吧,让我想起了昨天在4.1中比这还有过之的"高分".Select * 最好不要随便用,要本着用什么,选什么的原则, Select的字段越多,字段数据量越大,速度就越慢. 上面2种分页方式哪种都比单写这1句强多了,虽然看起来好像查询的次数更多一些,但实际上是以较小的代价换取了高效的性能,是非常值得的.

第1种方案同样可用于MS-SQL,而且可能是最好的.因为靠主键ID来定位起始段总是最快的.

Select Top 100 * From cyclopedia Where ID>=(
Select Top 90001 Max(ID) From (
 Select ID From cyclopedia Order By ID
) As tmp
)
但不管是实现方式是存贮过程还是直接代码中,瓶颈始终在于MS-SQL的TOP总是要返回前N个记录,这种情况在数据量不大时感受不深,但如果成百上千万,效率肯定会低下的.相比之下MySQL的limit就有优势的多,执行:

Select ID From cyclopedia limit 90000
Select ID From cyclopedia limit 90000,1
的结果分别是:
90000 rows in set (0.36) sec
1 row in set (0.06) sec
而MS-SQL只能用Select Top 90000 ID From cyclopedia 执行时间是390ms,执行同样的操作时间也不及MySQL的360ms.

mysql中limit与in不能同时使用的解决方式.的更多相关文章

  1. 解决mysql中limit和in不能同时使用的问题

    先给出数据表 CREATE TABLE `test_tb_grade` ( `ID` ) NOT NULL AUTO_INCREMENT, `) DEFAULT NULL, `COURSE` ) DE ...

  2. mysql中limit的用法实例解析

    mysql中limit的用法解析. 在mysql中,select * from table limit m,n.其中m是指记录开始的index,从0开始,n是指从第m条开始,取n条. 例如: mysq ...

  3. oracle数据库不支持mysql中limit功能

    oracle数据库不支持mysql中limit功能,但可以通过rownum来限制返回的结果集的行数,rownum并不是用户添加的字段,而是oracle系统自动添加的. (1)使查询结果最多返回前10行 ...

  4. (转载)mysql中limit用法

    (转载)http://hi.baidu.com/sppeivan/item/e45179375d6778c62f8ec221   mysql中limit用法 使用查询语句的时候,经常要返回前几条或者中 ...

  5. 第九章 MySQL中LIMIT和NOT IN案例

    第九章 MySQL中LIMIT和NOT IN案例 一.案例的项目 1.创建数据库语句: #创建数据库 CREATE DATABASE `schoolDB`; USE `schoolDB`; #创建学生 ...

  6. Mysql中limit的用法详解

    Mysql中limit的用法详解 在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,为我们提供了limit这样一个功能. SELECT * FROM table LIMIT [offset ...

  7. MySQL安装过程中出现“APPLY security settings错误”的解决方式

    ***********************************************声明*************************************************** ...

  8. prepareStatement设置参数,mysql数据出现中文‘?’的情景与解决方式

    在prepareStatement中传入中文的参数,mysql数据库中出现“?”号 try { Class.forName("com.mysql.jdbc.Driver"); co ...

  9. Django中的图片加载不出来解决方式记录

    背景:Python3.6 + Django2.2 在模板中的html文件中引用图片时,在浏览器中图片总是显示不出来,上网查了很多解决方式,但是都没有解决问题,最终尝试了多次后得以解决,但不清楚原理: ...

随机推荐

  1. java并发库--锁

    synchronized的缺陷: 被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,获取线程被阻塞时,没有释放锁会导致等待线 ...

  2. knockout_主页的demo复习

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  3. Lucene 4.7 --高亮显示

    jar lucene-analyzers-common-4.7.0.jar lucene-analyzers-smartcn-4.7.0.jar lucene-core-4.7.0.jar lucen ...

  4. python学习笔记4(对象/引用;多范式; 上下文管理器)

    ### Python的强大很大一部分原因在于,它提供有很多已经写好的,可以现成用的对象 21. 动态类型:对象/引用 对象和引用: 对象是储存在内存中的实体,对象名只是指向这一对象的引用(refere ...

  5. core dump gdb调试

    core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIG ...

  6. 了解 Nginx 基本概念

    前言 本篇是我学习 Nginx 的一些笔记,主要内容讲述了一些了解 Nginx 需要的基本概念.然后探讨一下 Nginx 的模块化的组织架构,以及各个模块的分类.工作方式.职责和提供的相关指令. 主要 ...

  7. iOS开发编码建议与编程经验

    作者:乞力马扎罗的雪(GitHub) 原文 在开发过程中,我们不仅要去看别人的代码,也要让别人看我们的代码.那么,有一个良好的编码习惯将会非常重要.下面将会罗列使用Objective-C来开发iOS的 ...

  8. Nagios安装部署和介绍(一)

    一.软件版本下载 Nagios版本下载地址: http://prdownloads.sourceforge.net/sourceforge/nagios/ http://sourceforge.net ...

  9. 初学structs2,结果类型简单示例

    一.自定义结果处理类,structs.xml中package节点下加result-types节点,在result-types节点下配置result-type的属性.然后在配置的action中的resu ...

  10. 自定义cell的步骤(每个cell的高度不一样,每个cell里面显示的内容也不一样)

    1.新建一个继承自UITableViewCell的子类  2. 在initWithStyle:方法中进行子控件的初始化 1> 将有可能显示的所有子控件都添加到contentView中 2> ...