【问题标题】:ORDER BY RAND() alternative [duplicate]ORDER BY RAND() 替代方案 [重复]
【发布时间】:2010-12-24 11:55:28
【问题描述】:

可能重复:
MySQL: Alternatives to ORDER BY RAND()

我目前有一个以 ORDER BY RAND(HOUR(NOW())) LIMIT 40 结尾的查询,以获得 40 个随机结果。结果列表每小时更改一次。

这会杀死查询缓存,从而损害性能。

您能否建议一种替代方法来获取不时变化的随机(ish)结果集?它不必是每个小时,也不必是完全随机的。

我更喜欢随机结果,而不是对表中的任意字段进行排序,但我会作为最后的手段...

(这是我想不时更新一下的新产品列表)。

【问题讨论】:

  • 你能提供一些关于你在编码什么的见解吗?我建议在代码而不是 sql 中执行此操作。
  • 你能在应用层缓存它吗?

标签: mysql performance random


【解决方案1】:

如果您有一个 ID 列,最好这样做:

-- create a variable to hold the random number
SET @rownum := SELECT count(*) FROM table;
SET @row := (SELECT CEIL((rand() * @rownum));

-- use the random number to select on the id column
SELECT * from tablle WHERE id = @row;

选择随机id号的逻辑可以移到应用层。

SELECT * FROM table ORDER BY RAND LIMIT 40

效率非常低,因为 MySQL 会处理表中的所有记录,对所有行执行全表扫描,随机排序。

【讨论】:

  • 该表可能在中间某处缺少 ID,因此正确的选择最终将是:SELECT * from tablle WHERE id >= @row LIMIT 1;
【解决方案2】:

它会杀死缓存,因为您每次都期望不同的结果集。您无法缓存一组随机值。如果你想缓存一组结果,缓存一个大的随机值集,然后在你将要使用这些值的时间的子部分内,在较小的集合中随机抓取 [在 sql 之外]。

【讨论】:

  • 好吧,我只希望它每隔一小时左右改变一次,所以在一小时内它会被修复。这是我当前的查询产生的结果,使用 rand() 的缺点是会阻止结果被缓存。
  • 好点 - 没必要依赖 MySQL 的 缓存;例如memcached 或其他应用层缓存都可以。
【解决方案3】:

我认为更好的方法是将产品标识符下载到中间层,在需要时随机选择 40 个值(每小时一次或每次请求)并在查询中使用它们:product_id in (@id_1, @id_2, ..., @id_40)

【讨论】:

  • +1 这通常是一个很好的解决方案,除非 @rikh 正在运行 Amazon 或 eBay(即数百万种产品)。将 ID 保存在内存中也可能对其他优化有用。
【解决方案4】:

您可能有一列包含每小时更新的随机值。

【讨论】:

    【解决方案5】:

    如果需要将大型数据集排序为随机顺序(确实需要排序),然后丢弃除前 40 条记录之外的所有记录,这将是一个非常讨厌的查询。

    更好的解决方案是只选择 40 条随机记录。有很多方法可以做到这一点,它通常取决于拥有均匀分布的密钥。

    另一种选择是在一个每小时只运行一次(或其他什么)的批处理作业中选择 40 条随机记录,然后记住它们是哪些。

    【讨论】:

      【解决方案6】:

      实现它的一种方法是打乱您将数据映射到的对象。如果您不将数据映射到对象,则可以将结果数组从数据库中洗牌。我不知道这是否会表现得更好,但您至少会从您提到的查询缓存中获得好处。

      您还可以生成一个从 1 到 n 的随机序列,并用这些索引结果数组(或对象数组)。

      【讨论】:

        【解决方案7】:

        在您的 PHP 代码中计算当前小时并将其传递给您的查询。这将产生一个可以缓存的静态值。

        请注意,您可能还有一个隐藏的错误。因为你只花一个小时,所以你只有 24 个不同的值,每天都会重复。这意味着今天下午 1 点显示的内容也将与明天 6 点显示的内容相同。您可能需要更改它。

        【讨论】:

        • 小时只是被用作随机数生成器的种子。是的,我知道我每天下午 2 点都会得到相同的结果,但这很好(除非产品列表发生任何变化)
        【解决方案8】:

        不要与缓存作斗争——利用它!

        按原样(甚至更简单)编写查询。然后,在您的代码中,缓存结果,将缓存到期时间设置为 1 小时。如果您使用的是缓存层,例如 memcached,那么您已经设置好了。如果没有,您可以构建一个相当简单的:

        [pseudocode]
        global cache[24]
        h = Time.hour
        if (cache[h] == null) {
          cache[h] = .. run your query
        }
        return cache[h];
        

        【讨论】:

          【解决方案9】:

          如果您每小时只需要一组新的随机数据,请不要访问数据库 - 将结果保存到应用程序的缓存层(或者,如果没有,只需将其放入临时某种文件)。查询缓存很方便,但如果您甚至不需要执行查询,那就更好了...

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-12-28
            • 1970-01-01
            相关资源
            最近更新 更多