【问题标题】:MySQL RAND() how often can it be used? does it use /dev/random?MySQL RAND() 多久可以使用一次?它使用 /dev/random 吗?
【发布时间】:2013-03-23 21:12:30
【问题描述】:

我有一个只有几行的表(前 50 行),我需要从表中获取随机值,我可以通过
ORDER BY RAND() LIMIT 1
做到这一点 主要问题是当我在 5 秒内有 6k 次选择时,rand stil 是否“可靠”? rand 是如何计算的,我可以随着时间的推移播种吗? (idk,每 5 秒一次)。

【问题讨论】:

  • ORDER BY RAND 最大的问题不是随机性(虽然是的,这是个问题),但事实上它会通过强制数据库扫描整个表并丢弃来杀死你的数据库性能它的所有索引。
  • 即使只有 50 行?我认为这没什么大不了的(是吗?)。
  • 不,如果您只是在谈论 50 行,那没什么大不了的。但同样,如果它只有 50 行,您可以轻松地将 mySQL 的 RAND 从等式中完全删除,方法是将全部加载到一个数组中并在 PHP 代码中对其进行排序。我接受 PHP 的随机也会有同样的问题,但你可以将 mySQL 的 ORDER BY RAND 与 PHP 的 array_rand() 结合起来,并使用两种不同的随机算法对其进行双重洗牌。这应该会给你更多的熵。
  • 直接在mysql中整理不是更好吗? (选择的随机化),我喜欢双重洗牌的想法,我正在考虑它,但我仍然没有用完随机值的风险吗? :) 那速度呢? mysql rand -> php rand 肯定比 mysql - rand
  • 是的,最好保存在mysql中。 PHP 选项只是真正发挥作用,因为它在表中的记录数量如此之少。如果您事先知道记录数,则另一个可行的选择是使用 PHP 随机化 LIMIT 子句,因为它是一个小表。但我想真正的答案取决于你得到一个真正随机(即不可预测的)结果的问题有多大:ORDER BY RAND 可能是完全明智的。它不是真正的随机,但如果你有多个人提出独立的请求,那么一个用户就很难独立预测。

标签: php mysql random


【解决方案1】:

MySQL 伪随机数生成器是完全确定的。文档说:

RAND() 并不是一个完美的随机生成器。这是一种按需生成随机数的快速方法,可在同一 MySQL 版本的平台之间移植。

它不能使用 /dev/random,因为 MySQL 被设计用于各种操作系统,其中一些操作系统没有 /dev/random。

MySQL 在服务器启动时使用time(0) 返回的整数初始化默认种子。 如果您对源代码行感兴趣,它位于文件 sql/mysqld.cc 中的 MySQL 源代码中,函数 init_server_components()。我认为它永远不会重新播种。

那么随后的“随机”数字完全基于种子。见源文件mysys_ssl/my_rnd.cc,函数my_rnd()


对于随机选择任务的最佳实践解决方案,无论是性能还是随机化质量,都是在最小主键值和最大主键值之间生成一个随机值。然后使用该随机值在表中选择一个主键:

SELECT ... FROM MyTable WHERE id > $random LIMIT 1

您使用 > 而不是 = 的原因是,由于行被删除或回滚,您的 id 可能存在间隙,或者您的 WHERE 子句中可能存在其他条件,因此行之间存在间隙符合你的条件。

这种大于法的缺点:

  • 在这种差距之后的行被选中的机会更高,差距越大,机会就越大。
  • 在生成随机值之前,您需要知道 MIN(id) 和 MAX(id)。
  • 如果您需要多个随机行,则效果不佳。

这种方法的优点:

  • 它比 ORDER BY RAND() 快得多,即使对于适度的表大小也是如此。
  • 您可以在 SQL 之外使用随机函数。

【讨论】:

  • 根据您的回答,mysqls rand 函数不能“用尽”随机值(因为它不是基于/dev/random),它纯粹基于初始种子,对吗? (无论我多快选择每 5 秒 6 到 12k)
  • 正确,这只是一个基于种子的等差数列。我无权在此处发布代码,但您应该下载 MySQL 的源代码并阅读我上面提到的 my_rnd() 函数。它只有三行长。
【解决方案2】:

RAND 是伪随机的。小心将其用于安全性。我不认为你的“从五十个中随机选择一排”是为了安全,所以你可能没问题。

对于一张小桌子来说速度相当快。从大表中选择随机行会很糟糕:它必须用伪随机数标记每一行,然后对它们进行排序。对于您描述的应用程序,@TheEwook 的建议是完全正确的;即使是对一个小表进行超过一毫秒一次的排序也会淹没强大的 MySQL 硬件。

永远不要播种 RAND,除非您正在测试,并且您想要一个可重复的随机数序列用于某种单元测试。我曾经在生成我认为难以猜测的会话令牌时学到了这一点。 MySQL 人员在 RAND 方面做得很好,您可以信任他们的应用程序。

我认为(不确定),如果你不给它播种,它会从 /dev/random 中的一个随机种子开始。

如果您需要加密级随机数,请自行阅读 /dev/random。但请记住,/dev/random 只能生成有限的速率。 /dev/urandom 使用 /dev/random 来生成更快的速率,但在其熵池中没有那么高级。

【讨论】:

  • 它不会用于任何安全问题,您的最后一段正是我写下这个问题的原因。 dev/random 不是“无穷无尽”(有限)我需要生成大量(“可靠”)rand 值,6k 只是一个猜测,但有时选择计数可能会飙升至 12k 或更多......
【解决方案3】:

如果您的表不是太大(假设最多 1000 条记录),那并不重要。但是对于大桌子,您必须选择另一种方式。

本文可能对您有所帮助:

http://www.titov.net/2005/09/21/do-not-use-order-by-rand-or-how-to-get-random-rows-from-table/

【讨论】:

  • 这个答案并没有真正解决关于RAND()的熵池的问题。
  • 感谢您的回答,但是 rand 是在哪里播种的,什么时候重新播种呢? (嗯,上面的评论很合适)。
猜你喜欢
  • 1970-01-01
  • 2018-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多