【问题标题】:High performance MySQL random non-sequential row高性能 MySQL 随机非顺序行
【发布时间】:2011-09-18 02:56:22
【问题描述】:

我正在尝试从数据不变的表中获取随机行。我读到有人尝试使用 ORDER BY RAND(),这对于大型数据集来说很糟糕,而且不能很好地扩展。

我还看到了让 SQL 在最小/最大范围之间获取随机行的解决方案,如下所示:FLOOR(MAX(needed_id) * RAND) 但这仅在行是连续的时才有效:1 ,2,3,4,5,6,7,8,9,10。

我需要提取的数据不是连续的,例如:1,2,3,4,10,11,12,13

所以我认为有两种解决方案:

第一种解决方案: 继续运行:FLOOR(MAX(needed_id) * RAND) 直到我收到正确类型的一行(1/6 机会)

第二个解决方案: 像这样创建一个重复的表(因为我的数据永远不会改变):

temp_id | needed_id | type 
1            1          1
2            4          1
3            7          2
3            8          2

所以我可以使用以下方法提取随机 temp_id:FLOOR(MAX(temp_id) * RAND) - WHERE type = 1

你怎么看?我可能会运行第一个解决方案大约 6 次,直到我收到正确的行,但在第二个解决方案中它会立即工作,但需要另一个表。

【问题讨论】:

  • 大数据集?多少行?
  • mmm 与大多数行相比可能没有那么多行,但应用程序需要快速运行:D 250k 行

标签: php mysql random rows nonsequential


【解决方案1】:

你的陈述

但这只有在行是连续的时候才有效:

并不完全正确:floor()max() 示例确实适用于非连续行,因为您会执行类似

的操作

WHERE id >= FLOOR(RAND()*MAX(id)) LIMIT 1

因此,您使用最接近您获得的随机命中的 ID。

这确实对直接在序列中的大间隙之后的命中有轻微的偏好,但这可能不会太糟糕,具体取决于您的数据集。

因此,根据您对这种轻微偏好有多少问题、您的数据集如何等等等等,这仍然可能是最好的解决方案。

因为有些人不清楚,所以函数的使用不是问题:

MAX 在索引字段上很快。您不需要计算所有行数(在 innoDB 上很慢),您只需要遍历您的 BTREE 索引,因此您会在log 时间找到这个值。这是近乎即时的

FLOOR 只是一个数学函数,它将在线性时间内执行。就像RAND。请注意,ORDER BY rand() 不是因为rand 而慢,而是因为您需要订购完整的表!这不是 rand 的问题,而是顺序的问题。

现在您有一个执行以下操作的查询:

WHERE id >= 48 LIMIT 1

这在索引字段上当然非常快。请记住,您不是通过执行任何类型的表扫描得到的 48(一个示例)。

【讨论】:

  • 嗨,对不起,我想这是我的错,我没有完全说明我的意思。我需要满足特定条件的行(例如 type=1)。因此,当它抓取 1 到 250,000 行之间的随机数时,它可能会选择一个没有类型 1 的随机行。虽然我猜你是对的......它会选择下一个最接近我的东西之后:)
  • 使用了 3 个函数,并且 RAND() 仍未从查询中删除。我不认为这个查询会快很多。
  • 请记住,您可以只添加WHERE type = 1。这只会使您的结果集更小(因此上限更大),但它仍然可以工作,并且您无需重复多次!
  • 一件事是 ORDER BY rand()。另一个是这样调用 RAND()。
  • @OZ_ 这些功能一点也不慢,它们的速度很快。 RANDFLOOR 是线性的,而在索引行上的 MAX 将在(猜测)类似于 log 的时间执行。 rand 的问题是当您使用ORDER BY rand() 时,因为您必须进行全表扫描。您不在此查询中。您将获得一个随机数,然后找到该数字最接近的 id(索引)。
【解决方案2】:

您应该阅读以下 Jan Kneschke 的博客文章:ORDER BY RAND()

他列出了一些可能的解决方案及其性能表现。

【讨论】:

  • 我非常喜欢这篇博文的深度,这样我可以更深入地了解它。
【解决方案3】:

$cnt = 行数。这个值可以被缓存(如果你使用 InnoDB,这是非常推荐的)。

$rnd = mt_rand(0,$cnt);

查询:

SELECT * FROM `table` WHERE `where_cond`='some_value' LIMIT $rnd,1

当然,您可以使用任何 where 子句选择任何值,所有技巧都在 LIMIT $rnd, 1 部分。
我喜欢这种方法,因为这里没有任何JOINs。
此外,此方法可用于已排序和未排序的行,即使没有 ID。

【讨论】:

  • 2 个查询而不是一个? ($cnt 需要查询)不好抱歉。
  • @yes123,哈哈。 1) count(*) 查询工作非常非常快; 2)计数值可以并且应该被缓存,尤其是data doesn't change时。
  • COUNT(*) 没有任何 where 条件只会在 MyIsam 上非常快,对于 InnoDB 来说并没有你想象的那么快
  • @Tobias P.,无论如何,即使在 InnoDB 中它也比 RAND() 快得多。这个值可以(并且应该)被缓存,所以忘记第二个查询。
  • @OZ_ 我确实编辑了您的答案,只是为了将$cnt 放入代码标签中。你已经回滚了。你是小孩还是?
猜你喜欢
  • 2023-04-08
  • 2015-11-23
  • 1970-01-01
  • 2012-05-18
  • 2017-07-18
  • 2021-01-13
  • 1970-01-01
  • 2019-04-04
  • 2013-04-23
相关资源
最近更新 更多