【问题标题】:query optimization to find random sample查询优化以找到随机样本
【发布时间】:2013-11-19 14:11:15
【问题描述】:

我有一个 sql 查询来随机选择 1200 条转发次数最多的推文,至少转发了 50 次,并且 tweetDate 应该比 4000 万条记录早 4 天。我在下面粘贴的查询有效,但需要 40 分钟,那么该查询有更快的版本吗?

SELECT 
    originalTweetId, Count(*) as total, tweetContent, tweetDate
FROM
    twitter_gokhan2.tweetentities
WHERE
    originalTweetId IS NOT NULL
        AND originalTweetId <> - 1
        AND isRetweet = true
    AND (tweetDate < DATE_ADD(CURDATE(), INTERVAL - 4 DAY))
GROUP BY originalTweetId
HAVING total > 50
ORDER BY RAND()
limit 0 , 1200;


---------------------------------------------------------------

Table creation sql is like:

    CREATE TABLE `tweetentities` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `tweetId` bigint(20) NOT NULL,
      `tweetContent` varchar(360) DEFAULT NULL,
      `tweetDate` datetime DEFAULT NULL,
      `userId` bigint(20) DEFAULT NULL,
      `userName` varchar(100) DEFAULT NULL,
      `retweetCount` int(11) DEFAULT '0',
      `keyword` varchar(500) DEFAULT NULL,
      `isRetweet` bit(1) DEFAULT b'0',
      `isCompleted` bit(1) DEFAULT b'0',
      `applicationId` int(11) DEFAULT NULL,
      `latitudeData` double DEFAULT NULL,
      `longitude` double DEFAULT NULL,
      `originalTweetId` bigint(20) DEFAULT NULL,
     PRIMARY KEY (`id`),
  KEY `index` (`originalTweetId`),
  KEY `index3` (`applicationId`),
  KEY `index2` (`tweetId`),
  KEY `index4` (`userId`),
  KEY `index5` (`userName`),
  KEY `index6` (`isRetweet`),
  KEY `index7` (`tweetDate`),
  KEY `index8` (`originalTweetId`),
  KEY `index9` (`isCompleted`),
  KEY `index10` (`tweetContent`(191))
) ENGINE=InnoDB AUTO_INCREMENT=41501628 DEFAULT CHARSET=utf8mb4$$

【问题讨论】:

  • 信息不足,很难理解你的表是什么样子的,其中的键是如何使用的等等。请提供数据库结构,一些示例数据。
  • 我也刚刚添加了表结构。
  • 不是twitter数据库吗?
  • 是的,有点。我正在收集基于特定关键字的推文进行分析。
  • 这个WHERE 子句破坏了索引的使用。 originalTweetId IS NOT NULL

标签: mysql sql random-sample


【解决方案1】:

当然,您是在汇总 大量 条记录,然后将它们随机化。这种东西很难快速做出来。回到时间的开始会使情况变得更糟。搜索空条件只会将其丢弃。

如果您希望它合理地执行,您必须去掉IS NOT NULL 选择。否则,它将表现不佳。

但是让我们试着找到一个合理的解决方案。首先,让我们获取我们需要的originalTweetId 值。

 SELECT MIN(id) originalId,
        MIN(tweetDate) tweetDate,
        originalTweetId, 
        Count(*) as total
   FROM twitter_gokhan2.tweetentities
  WHERE originalTweetId <> -1 
  /*AND originalTweetId IS NOT NULL We have to leave this out for perf reasons */
    AND isRetweet = true
    AND tweetDate < CURDATE() - INTERVAL 4 DAY
    AND tweetDate > CURDATE() - INTERVAL 30 DAY  /*let's add this, if we can*/
  GROUP BY originalTweetId
 HAVING total >= 50

此摘要查询为我们提供数据库中每个主题推文的最低 ID 号和日期。

为了让它快速运行,我们需要一个复合索引 (originalTweetId, isRetweet, tweetDate, id)。该查询将在 tweetDate 上对该索引进行范围扫描,这与您希望的一样快。调试此查询,以确保正确性和性能,然后继续。

现在进行随机化。让我们用尽可能少的数据来做这件事,以避免对大量的东西进行排序。

 SELECT originalTweetId, tweetDate, total, RAND() AS randomOrder
   FROM (
    SELECT MIN(id) originalId,
           MIN(tweetDate) tweetDate
           originalTweetId, 
           Count(*) as total
      FROM twitter_gokhan2.tweetentities
     WHERE originalTweetId <> -1 
     /*AND originalTweetId IS NOT NULL We have to leave this out for perf reasons */
       AND isRetweet = true
       AND tweetDate < CURDATE() - INTERVAL 4 DAY
       AND tweetDate > CURDATE() - INTERVAL 30 DAY  /*let's add this, if we can*/
      GROUP BY originalTweetId
     HAVING total >= 50
   ) AS retweets
  ORDER BY randomOrder
  LIMIT 1200

太好了。现在我们有一个随机排列的 1200 个推文 ID 和日期的列表。现在让我们去获取内容。

 SELECT a.originalTweetId, a.total, b.tweetContent, a.tweetDate
   FROM (
          /* that whole query above */
        ) AS a
   JOIN twitter_gokhan2.tweetentities AS b ON (a.id = b.id)
  ORDER BY a.randomOrder

看看这是怎么回事?使用复合索引进行汇总,并在最少的数据量上进行。然后进行随机化,然后去获取你需要的额外数据。

【讨论】:

  • RAND() 移动到同一查询的列位置真的可以避免全表扫描吗? (典型的ORDER BY RAND() 会导致表扫描和可能的临时表,请参阅:stackoverflow.com/questions/4329396/…。直观地说,将其放在子查询中可能会有所帮助,因为它已经在该级别进行了分组,但它似乎是按位置排列的列与顺序应该没关系。)
  • 我将 RAND() 放入子查询中以确保保留外部查询的顺序。关于记住查询结果的顺序在缺席或 ORDER BY 子句中是不可预测的,我有点学究。
  • 功能位置(哪个查询)看起来不错。我在问你为什么把它从ORDER BY RAND() 改为RAND() AS foo 然后ORDER BY foo。我想知道这是否会对性能产生任何影响。
  • 我没有测试过。以LIMIT 1200 结尾的查询仍然必须考虑每一行,以便行采样是正确随机的。所以这里没有巨大的性能提升。我只是这样做以使订购稳定。
  • 是的,这里没有办法避免使用临时表。我们正在处理摘要子查询的结果集。
【解决方案2】:

您通过选择每条超过 4 天的记录来选择大量记录......

由于查询需要大量时间,为什么不简单地使用在后台重复运行的独立脚本准备结果....

您可以假设如果它是转发,则 originalTweetId 不能为 null/-1

【讨论】:

  • 查看修改后的想法。我假设您正在为网站准备这些数据,因此只需在后台不断重新运行该过程即可。
  • 一个应用程序正在为每条记录查找 originalTweetId 并更新该字段;但是,如果由于受保护的推文帐户问题或已删除的推文问题而无法访问元数据,我在此字段中输入 -1。
  • 为什么不建立一个运行和更新 retweetCount 的自动化流程,以便您可以使用它?
【解决方案3】:

只是为了澄清...您真的是要查询超过 4 天的所有内容吗?

 AND (tweetDate < DATE_ADD(CURDATE(), INTERVAL - 4 DAY))

或者...您的意思是您只想汇总过去 4 天内最近的推文。对我来说,两年前的推文对于时事来说毫无价值......如果是这样的话,你可能会更好地改为

 AND (tweetDate >= DATE_ADD(CURDATE(), INTERVAL - 4 DAY))

【讨论】:

  • 我正在寻找至少 4 天前的推文,而不是 4 天内的当前推文。
【解决方案4】:

看看这是否比 40 分钟快一点:

首先测试没有注释的行,然后重新添加它们以比较性能影响。 (尤其是ORDER BY RAND() 被认为是可怕的)

SELECT
    originalTweetId,
    total,
--    tweetContent,  -- may slow things somewhat
    tweetDate
FROM (
  SELECT
    originalTweetId,
    COUNT(*) AS total,
--    tweetContent,  -- may slow things somewhat
    MIN(tweetDate) AS tweetDate,
    MAX(isRetweet) AS isRetweet
  FROM twitter_gokhan2.tweetentities
  GROUP BY originalTweetId
) AS t
WHERE originalTweetId > 0
  AND isRetweet
  AND tweetDate < DATE_ADD(CURDATE(), INTERVAL - 4 DAY)
  AND total > 50
-- ORDER BY RAND()  -- very likely to slow performance,
                    -- test with and without...
LIMIT 0, 1200;

PS - originalTweetId 应该被索引

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多