今天看到一篇很好的文章 优化Mysql随机取出N条记录。 有时候会遇到 比如从一张表中随机取出N条 不重复 记录的需求,然后我以前都是上去就是这样 SELECT * FROM table ORDER BY RAND() LIMIT 1
今天发现这个有个性能漏洞 当数据量大的时候 会吓死你 下面我准备了一张3000W数据的表 就2个字段 来模拟下优化
先来分析下需求 一般这种需求有两种实现方式
使用程序处理
当数据量很大的时候 其实可以通过随机数在程序中随机生成N个ID 然后一条SQL select * from uuid_test where id in (100000,232323,23232322,23232366);
直接获取到结果
看下他的性能 使用 explain extended sql
, extended 参数可以显示更详细的信息 不加也可以,如果后面还有 show warnings
的话还可以显示Mysql优化的SQL过程 虽然不一定是最终执行的优化SQL
使用了 using where
但是 预计遍历的 row=4
很少 性能还凑合 不算太差
使用Mysql处理
如果使用Mysql处理的话 又会有几种情况需要区分。不过在此之前先来看下我经常写的那个SQL的性能
explain select * from uuid_test order by RAND() limit 1;
你不要尝试直接查询 我试了下 查询近2分钟 直接用 explain 来分析
可以看到这条SQL 使用了索引 使用了临时表 使用了文件排序 并且是全表扫描 3000多W行。 下面讲下优化
如果ID是连续分布的
如果ID属性在数据库中是连续分布的 可以使用 Max()
+ RAND()
来获取随机ID
explain select CEIL(RAND() * MAX(id)) from uuid_test;
上面因为 RAND() * MAX(id)
的结果是一个小数 可以通过 CEIL
或者 FLOOR
来取整
因为是主键 使用了一个索引 虽然这样会加速查询 但它同时还是全表扫描 我测了下基本要 6S 左右 还有很大的优化空间
explain select CEIL(RAND() * (select MAX(id) from uuid_test));
使用了子查询后 性能回到了等同于 单查询 max
的程度 看上面分析 分别是没有使用表 和 最优化查询 然后测试了下 执行时间 0.005S 比上面差距了 1000 倍… (不知道有没有算错…)
经过上面已经拿到了 ID 下一步就是获取对应的记录了 用了个常规查询 直接 id = x
然后又死了
explain select * from uuid_test where id = (select CEIL(RAND() * (select MAX(id) from uuid_test)));
性能如下
虽然子查询中已经很快了 但是外面的 where 导致了使用where使用索引 并且全表扫描。经过测试 查询时间为 10S 左右
可以通过 show warnings;
来获取警告信息 顺便说下 show warnings;
是什么 根据官方文档说明
是一个诊断语句,显示有关在当前会话中执行语句所产生的条件(错误,警告和注释)的信息
在EXPLAIN之后也使用SHOW WARNINGS来显示EXPLAIN生成的扩展信息
必须换一种方法确保随机ID只被生成一次
explain SELECT * FROM uuid_test JOIN (SELECT CEIL(RAND() * (SELECT MAX(id) FROM uuid_test)) AS id ) AS r2 USING (id);
SQL换成这样后只选择了一行 没有排序没有应用 查询被大部分优化
如果ID是不连续的
考虑数据库ID是不连贯的情况,可以根据随机的ID 然后取出比它或者小的数据 然后 Limit
限制返回的条数
explain SELECT * FROM uuid_test as r1 JOIN (SELECT (RAND() * (SELECT MAX(id) FROM uuid_test)) AS id ) AS r2 where r1.id >= r2.id limit 10;
这条SQL的性能和上面基本差不多 关键在于外面的where查询上 有可能会遍历半个表 因为我又3000多W数据 它预计的差不多1500W的样子 即使这样 他的速度也是很快
不过他取出来的结果是在那个ID上或下 连续的10条记录…好像没啥办法优化成完全不规则随机
完全随机N条
如果想完全随机N条记录的话 数据量很大的时候还是建议在程序里随机记录ID吧
或者建立一个映射表把不连贯的ID都存进去变成连贯
|
|
差不多就这样了
原文地址
http://jan.kneschke.de/projects/mysql/order-by-rand/ 英文
http://shiningray.cn/order-by-rand.html 中文