Author:flymorn Source:飘易
Categories:PHP编程 PostTime:2011-1-14 15:35:07
正 文:

最近由于需要研究了一下MYSQL的随机抽取实现方法。举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是:

SELECT * FROM content ORDER BY RAND() LIMIT 1


【飘易注:3万条记录查询花费 0.3745 秒(下同);从mysql slow query log看出“ORDER BY RAND() ”全表扫描了2次!】
后来我查了一下MYSQL的官方手册,里面针对RAND()的提示大概意思就是,在ORDER BY从句里面不能使用RAND()函数,因为这样会导致数据列被多次扫描。但是在MYSQL 3.23版本中,仍然可以通过ORDER BY RAND()来实现随机。
但是真正测试一下才发现这样效率非常低。一个15万余条的库,查询5条数据,居然要8秒以上。查看官方手册,也说rand()放在ORDER BY 子句中会被执行多次,自然效率及很低。
搜索Google,采用JOIN,查询max(id) * rand()来随机获取数据。

SELECT * 
FROM `content` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `content`)) AS id) AS t2 
WHERE t1.id >= t2.id 
ORDER BY t1.id ASC LIMIT 1; 

【查询花费 0.0008 秒,飘易认为可以推荐使用这个语句!!】
但是这样会产生连续的5条记录。解决办法只能是每次查询一条,查询5次。即便如此也值得,因为15万条的表,查询只需要0.01秒不到。
有一个方法:

SELECT * FROM `content` AS a JOIN ( SELECT MAX( ID ) AS ID FROM `content` ) AS b ON ( a.ID >= FLOOR( b.ID * RAND( ) ) ) LIMIT 5;  

上面这种方式保证了一定范围内的随机,查询花费 0.4265 秒,也不推荐。
下面的语句,mysql的论坛上有人使用

SELECT * 
FROM `content` 
WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `content` ) 
ORDER BY id LIMIT 1;

【查询花费 1.2254 秒,飘易强烈不推荐!因为实测后,3万行的表,这个语句竟然会扫描500万行!!】
跟上面的语句还是有很大差距。总觉有什么地方不正常。于是我把语句改写了一下。

SELECT * FROM `content` 
WHERE id >= (SELECT floor(RAND() * (SELECT MAX(id) FROM `content`)))  
ORDER BY id LIMIT 1; 

【查询花费 0.0012 秒】
这下,效率又提高了,查询时间只有0.01秒
最后,再把语句完善一下,加上MIN(id)的判断。我在最开始测试的时候,就是因为没有加上MIN(id)的判断,结果有一半的时间总是查询到表中的前面几行。
完整查询语句是:

SELECT * FROM `content` 
WHERE id >= (SELECT floor( RAND() * ((SELECT MAX(id) FROM `content`)-(SELECT MIN(id) FROM `content`)) + (SELECT MIN(id) FROM `content`)))  
ORDER BY id LIMIT 1; 

【查询花费 0.0012 秒】

SELECT * 
FROM `content` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `content`)-(SELECT MIN(id) FROM `content`))+(SELECT MIN(id) FROM `content`)) AS id) AS t2 
WHERE t1.id >= t2.id 
ORDER BY t1.id LIMIT 1;

【查询花费 0.0008 秒】
最后在php中对这两个语句进行分别查询10次,
前者花费时间 0.147433 秒
后者花费时间 0.015130 秒
看来采用JOIN的语法比直接在WHERE中使用函数效率还要高很多。(via)
======================================
【好了,最后飘易来总结下】
第一种方案,即原始的 Order By Rand() 方法:

$sql="SELECT * FROM content ORDER BY rand() LIMIT 12";
$result=mysql_query($sql,$conn);
$n=1;
$rnds='';
while($row=mysql_fetch_array($result)){
$rnds=$rnds.$n.". <a href='show".$row['id']."-".strtolower(trim($row['title']))."'>".$row['title']."</a><br />\n";
$n++;
}

3万条数据查12条随机记录,需要0.125秒,随着数据量的增大,效率越来越低。
第二种方案,改进后的 JOIN 方法:

for($n=1;$n<=12;$n++){
$sql="SELECT * FROM `content` AS t1 
JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `content`)) AS id) AS t2 
WHERE t1.id >= t2.id ORDER BY t1.id ASC LIMIT 1";
$result=mysql_query($sql,$conn);
$yi=mysql_fetch_array($result);
$rnds = $rnds.$n.". <a href='show".$yi['id']."-".strtolower(trim($yi['title']))."'>".$yi['title']."</a><br />\n";
}

3万条数据查12条随机记录,需要0.004秒,效率大幅提升,比第一种方案提升了约30倍。缺点:多次select查询,IO开销大。
第三种方案,SQL语句先随机好ID序列,用 IN 查询(飘易推荐这个用法,IO开销小,速度最快):

$sql="SELECT MAX(id),MIN(id) FROM content";
$result=mysql_query($sql,$conn);
$yi=mysql_fetch_array($result);
$idmax=$yi[0];
$idmin=$yi[1];
$idlist='';    
for($i=1;$i<=20;$i++){    
if($i==1){ $idlist=mt_rand($idmin,$idmax); }    
else{ $idlist=$idlist.','.mt_rand($idmin,$idmax); }    
}  
$idlist2="id,".$idlist;
$sql="select * from content where id in ($idlist) order by field($idlist2) LIMIT 0,12"; 
$result=mysql_query($sql,$conn);
$n=1;
$rnds='';
while($row=mysql_fetch_array($result)){
$rnds=$rnds.$n.". <a href='show".$row['id']."-".strtolower(trim($row['title']))."'>".$row['title']."</a><br />\n";
$n++;
}

3万条数据查12条随机记录,需要0.001秒,效率比第二种方法又提升了4倍左右,比第一种方法提升120倍。注,这里使用了 order by field($idlist2) 是为了不排序,否则 IN 是自动会排序的。缺点:有可能遇到ID被删除的情况,所以需要多选几个ID。
测试方法:

$t = microtime(true); 
//执行语句
echo microtime(true) - $t;  

转载于:https://www.cnblogs.com/zengkefu/p/5725918.html

改进MySQL Order By Rand()的低效率相关推荐

  1. [mysql] MySQL Order By Rand()效率【转载】

    最近由于需要大概研究了一下MYSQL的随机抽取实现方法.举个例子,要从tablename表中随机提取一条记录,大家一般的写法就是:SELECT * FROM tablename ORDER BY RA ...

  2. mysql order by rand 优化_mysql order by与by rand() 的优化经验

    介绍下MySQL中的order by语句. 几种order by的情况 从最简单的case开始看起. 用这个表来说明:(10w行数据) 1.  最简单的order ―― order by索引字段 从e ...

  3. mysql order by random,sql-MySQL:ORDER BY RAND()的替代方法

    我正在使用的解决方案也发布在下面的链接中:我如何优化MySQL的ORDER BY RAND()函数? 我假设您的用户表将大于您的配置文件表,如果不是,则为1到1的基数. 如果是这样,我将首先在用户表上 ...

  4. c++ sort 从大到小排序_算法的艺术:MySQL order by对各种排序算法的巧用

    在 [精华]洞悉MySQL底层架构:游走在缓冲与磁盘之间 这篇文章中,我们介绍了索引树的页面怎么加载到内存中,如何淘汰,等底层细节.这篇文章我们从比较宏观的角度来看MySQL中关键字的原理.本文,我们 ...

  5. MySQL中的RAND()函数使用详解

    转自:https://www.jb51.net/article/66697.htm MySQL RAND()函数调用可以在0和1之间产生一个随机数: ? 1 2 3 4 5 6 7 mysql> ...

  6. 优化SQL步骤——查看SQL执行频率 || 定位低效率执行SQL

    优化SQL步骤 在应用的的开发过程中,由于初期数据量小,开发人员写 SQL 语句时更重视功能上的实现, 但是当应用系统正式上线后,随着生产数据量的急剧增长,很多 SQL 语句开始逐渐显露出性能问题,对 ...

  7. mysql order by newid_order by newid() 随机查询

    在日常作业中,有时候可能是一些活动要抽出得奖人或选出抽查的一些名单,就常常会使用到Order BY Newid() 的方式来做随机数选出,但有可能的状况需是要搭配到DISTINCT 来选出,这时候如D ...

  8. 科技作者吴军:不用低效率的算法做事情

    世界上总有一些IT难题,需要有经验的人解决."如今已55岁的吴军,认为年龄与能力共同成长才是应对"35岁危机"之道,在过去的职业生涯中,吴军正是这样做的.本文,吴军通过讲 ...

  9. 吴军亲述编程生涯:不用低效率的算法做事情

    作者 | 吴军       责编 | 田玮靖 出品 | <新程序员>编辑部 世界上总有一些IT难题,需要有经验的人解决.如今已55岁的吴军,认为年龄与能力共同成长才是应对"35岁 ...

最新文章

  1. vi vim 插入 删除 修改 文本
  2. arxiv.org经常打不开真是让人头大
  3. 计算机怎么把硬盘分成几个,如何把电脑的一个盘的容量分给另外一个盘
  4. 除权除息对散户是利好还是不利好?
  5. 排序算法专题-选择排序
  6. tp1900芯片对比7621a_TP无线路由器WDR7660千兆版,厉害了单芯片TP1900
  7. java将页面转为pdf和pdf上添加盖章
  8. Mysql重复数据查重保留一条
  9. linux管道通信原理
  10. 第五章:数学运算-fractions:有理数-创建Fraction实例
  11. 【真相】网易暴力裁员事件 企业暴力开除重病员工事件绝非孤例
  12. Java封装继承多态练习题
  13. ntpd、ntpdate的区别
  14. CSS list-style样式集锦
  15. 『十年树木 百年树人』最好的机会AI最牛的你!
  16. python中的imp模块——让引用模块更加简单
  17. HDFS报错datanode.DataNode (DataXceiverServer.java:run(168))
  18. 游戏服务端之屏蔽字检测
  19. 什么是GSP药品经营管理规范?
  20. 工业智能网关BL110应用之二十一: 如何添加LAN口采集的设备

热门文章

  1. 第一章 处理器体系结构
  2. React开发(264):react使用国际化
  3. [html] 为什么我们要使用web workers?
  4. [vue] 说说你对vue的template编译的理解?
  5. [css] 你知道css的预处理器和后处理器都有哪些吗?它们有什么区别呢?
  6. [css] 如何解决IE6浮动时产生双倍边距的BUG?
  7. [vue-cli]在使用vue-cli开发vue项目时,自动刷新页面的原理你了解吗?
  8. 前端学习(2823):sitemap配置
  9. GY歌谣之读懂每行代码(飞智) 2020 10 16 Duplicate keys detected
  10. 前端学习(1865)vue之电商管理系统电商系统之实现表单的数据绑定