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:ORDER BY RAND() 的替代方案

MySQL 查询优化与 group by 和 order by rand

改进MySQL Order By Rand()的低效率

ORDER BY RAND()函数在mysql中执行需要很长时间[重复]

MySQL ORDER BY rand(),名称为 ASC

PHP,MySQL - 结果数组洗牌会比“select ... order by rand()”更快吗?