【问题标题】:mysql order by rand() performance issue and solutionmysql order by rand() 性能问题及解决方案
【发布时间】: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

上述查询速度太慢,导致服务器负载过重

【问题讨论】:

标签: php mysql


【解决方案1】:

首先,都生成一个从 1 到 MAX(id) 的随机值,而不是 100000000。

那么至少有几个很好的解决方案:

  1. 使用> 而不是=

    SELECT items FROM tablea where status='0' and id>'$id23' LIMIT 1
    

    (status,id,items) 上创建索引,使其成为仅索引查询。

  2. 使用=,但如果没有找到匹配项,请使用不同的随机值重试。有时需要多次尝试,但通常只需要一次尝试。 = 应该更快,因为它可以使用主键。如果它更快并且在 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 条记录。
猜你喜欢
  • 2016-03-10
  • 2010-12-24
  • 2012-06-03
  • 1970-01-01
  • 2014-06-11
  • 1970-01-01
  • 2012-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多