mysql order by rand() 性能问题及解决方案
Posted
技术标签:
【中文标题】mysql order by rand() 性能问题及解决方案【英文标题】:mysql order by rand() performance issue and solution 【发布时间】:2013-01-15 03:40:26 【问题描述】:我使用 rand() 的顺序从数据库生成随机行没有任何问题,但我意识到随着数据库大小的增加,这个 rand() 会导致服务器负载过重,所以我正在寻找替代方案,我尝试通过生成使用 php rand() 函数的一个随机数并将其作为 id 放在 mysql 查询中,它非常快,因为 mysql 知道行 id 但问题出在我的表中,所有数字都不可用。例如 1、2、5、9、12 之类的。
如果 php rand() 生成数字 3,4 等,则查询将为空白,因为没有数字 3、4 等的 id。
从 php 生成随机数的最佳方法是什么,但它应该在该表中生成可用的 no,因此它必须检查该表。请告知。
$id23=rand(1,100000000);
SELECT items FROM tablea where status='0' and id='$id23' LIMIT 1
上述查询速度很快,但有时会生成数据库中不可用的 no。
SELECT items FROM tablea where status=0 order by rand() LIMIT 1
上述查询速度太慢,导致服务器负载过重
【问题讨论】:
看看这里:: ***.com/questions/1244555/… 您(已经)知道行数还是需要第二次查询才能获得? 【参考方案1】:首先,都生成一个从 1 到 MAX(id) 的随机值,而不是 100000000。
那么至少有几个很好的解决方案:
使用>
而不是=
SELECT items FROM tablea where status='0' and id>'$id23' LIMIT 1
在(status,id,items)
上创建索引,使其成为仅索引查询。
使用=
,但如果没有找到匹配项,请使用不同的随机值重试。有时需要多次尝试,但通常只需要一次尝试。 =
应该更快,因为它可以使用主键。如果它更快并且在 90% 的时间内一次尝试就可以完成,那么当需要多次尝试时,这可能会弥补另外 10% 的时间。取决于您的 id 值有多少差距。
【讨论】:
@bill-karvin 所以我应该使用 > 或 = 因为你建议 > 会很慢 无论如何,它都比 ORDER BY RAND() 好得多。 :-) @raviloves 第一个应该快一点(因为没有重新运行的机会),但随机性稍差,具体取决于 id 中的间隙位置。 @Jim,对,间隔后面的 id 值被更频繁地选择。但是对于某些应用来说,不完全随机的选择可能仍然足够。 如果你使用 >,你需要一个ORDER BY
,否则它会破坏随机数的目的。此外,INDEX(status, id)
将进一步提高 this 情况下的速度。【参考方案2】:
使用您的数据库从表中找到最大值,生成一个小于或等于该值的随机数,获取 id 大于或等于您的随机数的第一行。无需 PHP。
SELECT items
FROM tablea
WHERE status = '0' and
id >= FLOOR(1 + RAND() * (SELECT MAX(id) FROM tablea))
LIMIT 1
【讨论】:
谢谢!主要的好处是不必为了确保您没有低于/超过说明表的最大 id 而两次访问数据库。 这个特殊的解决方案有一个缺点,它有利于表中早期的行。它会反复扫描测试表id
与重新评估的表达式,直到成功。
我对 MySQL 不太熟悉,但这不就是索引扫描吗?甚至可能仅在索引打开时才扫描索引(id,状态,项目)
有两个步骤 -- 评估子查询一次;它需要id
上的索引(大概是PRIMARY KEY(id)
);这是非常有效的。然后它扫描表的一部分。 INDEX(status, id, items)
,按照 this 的顺序,是最优的并且“覆盖”。问题是结果将在status=0
字符串之后和/或id
中的空白之后“首选”项目。它也可能不返回任何内容 - 当 RAND()
太接近 1.0 时。【参考方案3】:
您是对的,如果您正在处理大型数据集,ORDER BY RAND()
不是一个好的解决方案。根据需要随机化的频率,您可以生成一个带有随机数的列,然后以某个预定义的时间间隔更新该数字。
您可以将该列用作您的排序索引。这适用于重读环境,并在一段时间内产生可预测的随机顺序。
【讨论】:
我在想你建议的事情。我想我想讨论什么。我将从大数据库生成行 ID 并存储在其他表中,我将保留最多 1000 个数字,我将从该表中生成随机数,并在工作完成后删除该行。它是怎么回事??????【参考方案4】:一个可能的解决方案是使用limit:
$id23=rand(1,$numberOfRows);
SELECT items FROM tablea where status='0' LIMIT $id23 1
这不会产生任何遗漏的行(但正如 hek2mgl 所提到的)需要知道选择中的行数。
【讨论】:
我也建议这样做 :) 但不确定行数是否已经可用 @hek2mgl 我已经清楚地写过我正在使用这个,但问题是所有行都不可用,这种方法有时会生成不可用的数字,在这种情况下查询将为空白 事实证明这并不快,因为它必须扫描 $id23 行来进行偏移。获取行数的 COUNT() 也不是一个快速的操作。 @BillKarwin 呵呵,我以为 MySQL 会想出一个聪明的方法来跳入数据集的中间。这是否意味着对于大型数据集,最好使用 id 进行分页? @BillKarwin 啊,当然。如果您不知道前 4 条记录是什么,则无法找到第 5 条记录。以上是关于mysql order by rand() 性能问题及解决方案的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 查询优化与 group by 和 order by rand