【问题标题】:How to improve order by performance with joins in mysql如何通过 mysql 中的连接提高性能顺序
【发布时间】:2017-09-05 10:57:58
【问题描述】:

我正在开发一个社交网络跟踪应用程序。即使是连接也可以在适当的索引下正常工作。但是当我添加 order by 子句时,总查询的执行时间要长 100 倍。以下查询是我用来获取没有 order by 子句的 twitter_users 的。

SELECT DISTINCT  `tracked_twitter`.id
FROM tracked_twitter
INNER JOIN  `twitter_content` ON  `tracked_twitter`.`id` = `twitter_content`.`tracked_twitter_id` 
INNER JOIN  `tracker_twitter_content` ON  `twitter_content`.`id` = `tracker_twitter_content`.`twitter_content_id` 
AND  `tracker_twitter_content`.`tracker_id` =  '88'
LIMIT 20

显示第 0 - 19 行(共 20 行,查询耗时 0.0714 秒)

但是当我添加 order by 子句(在索引列上)

SELECT DISTINCT  `tracked_twitter`.id
FROM tracked_twitter
INNER JOIN  `twitter_content` ON  `tracked_twitter`.`id` =  `twitter_content`.`tracked_twitter_id` 
INNER JOIN  `tracker_twitter_content` ON  `twitter_content`.`id` =  `tracker_twitter_content`.`twitter_content_id` 
AND  `tracker_twitter_content`.`tracker_id` =  '88'
ORDER BY tracked_twitter.followers_count DESC 
LIMIT 20

显示第 0 - 19 行(共 20 行,查询耗时 13.4636 秒)

解释

当我单独在其表中实现 order by 子句时,不需要太多时间

SELECT * FROM `tracked_twitter` WHERE 1 order by `followers_count` desc limit 20

显示第 0 - 19 行(共 20 行,查询耗时 0.0711 秒)[followers_count: 68236387 - 10525612]

建表查询如下

CREATE TABLE IF NOT EXISTS `tracked_twitter` (
    `id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `handle` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `location` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `description` text COLLATE utf8_unicode_ci,
    `profile_image` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `followers_count` int(11) NOT NULL,
    `is_influencer` tinyint(1) NOT NULL DEFAULT '0',
    `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
    `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
    `gender` enum('Male','Female','Other') COLLATE utf8_unicode_ci 
     DEFAULT NULL,
     PRIMARY KEY (`id`),
     KEY `followers_count` (`followers_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

所以当我在它的表上执行它时,join 并没有因为运行良好而减慢查询和排序。那么如何提高性能呢?

更新 1

@GordonLinoff 方法解决了我是否只需要父表中的结果集。我想知道每人的推文数量(与 tracked_twitter 表匹配的 twitter_content 计数)。我该如何修改它?如果我想对推文内容进行数学运算,我该怎么做??

SELECT  `tracked_twitter` . * , COUNT( * ) AS twitterContentCount, retweet_count + favourite_count + reply_count AS engagement
FROM  `tracked_twitter` 
INNER JOIN  `twitter_content` ON  `tracked_twitter`.`id` =  `twitter_content`.`tracked_twitter_id` 
INNER JOIN  `tracker_twitter_content` ON  `twitter_content`.`id` =  `tracker_twitter_content`.`twitter_content_id` 
WHERE  `is_influencer` !=  '1'
AND  `tracker_twitter_content`.`tracker_id` =  '88'
AND  `tracked_twitter_id` !=  '0'
GROUP BY  `tracked_twitter`.`id` 
ORDER BY twitterContentCount DESC 
LIMIT 20 
OFFSET 0

【问题讨论】:

  • 如果删除LIMIT-clause,无序查询需要多长时间? LIMIT 在无序结果集中基本上意味着“给我一些符合我标准的 20 条记录”,而在有序结果中它意味着“给我找符合我标准的前 20 条记录”,这基本上意味着你必须识别所有其中。
  • EXPLAIN 在查询中的输出是什么?
  • @raina77ow 我添加了有问题的解释图片
  • 其他两张表请提供SHOW CREATE TABLE。听起来您好像缺少INDEX(tracker_id)。有关如何使 many:many 高效的信息,请参阅此内容:mysql.rjweb.org/doc.php/…

标签: mysql sql performance sql-order-by inner-join


【解决方案1】:

尝试摆脱distinct。那是性能杀手。我不确定为什么您的第一个查询会很快起作用;也许 MySQL 足够聪明,可以将其优化掉。

我会尝试:

SELECT tt.id
FROM tracked_twitter tt
WHERE EXISTS (SELECT 1
              FROM twitter_content tc INNER JOIN  
                   tracker_twitter_content ttc
                   ON  tc.id =  ttc.twitter_content_id
              WHERE  ttc.tracker_id =  88 AND
                     tt.id =  tc.tracked_twitter_id
             )
ORDER BY tt.followers_count DESC ;

对于此版本,您需要以下索引:tracked_twitter(followers_count, id)twitter_content(tracked_twitter_id, id)tracker_twitter_content(twitter_content_id, tracker_id).

【讨论】:

  • 它运行良好,并且 order by 子句也没有减慢查询的执行速度(总共 20 个,查询耗时 0.0707 秒)。如果您解释查询,我会很高兴。这样我就会得到关于这种方法的知识。如果您有任何参考链接可以阅读这些方法,那将会很有用。谢谢你的回答。
  • 我想第一个查询运行得很快,因为 MySQL 知道它只需要从一个无序集中收集 20 条不同的记录,这可能来自少量的行(假设超过 20 条但少于千)。从数百万个有序条目中获取前 20 个不同的条目会慢很多——不同的要么有点像红鲱鱼,要么是 orderby 需要时间,或者 MySQL 可能愚蠢到不仅可以排序,而且然后在仅返回 20 之前区分数百万行
  • @CaiusJard 。 . .实际上,我认为 MySQL 可以使用id 上的索引来防止重复——它只是按顺序读取索引。使用明确的ORDER BY,这是不可能的。
  • 对 88 以外的 id 尝试此查询;您可能会发现它的性能不一致。
  • 我对这个查询有一个小问题。如果我想知道每人的推文数量(与 tracked_twitter 表匹配的 twitter_content 计数)。我该如何修改它?如果我想对推文内容进行数学运算,我该怎么做? @戈登林诺夫。我用示例查询更新了问题。
【解决方案2】:

父表保持在有限制的括号上

SELECT DISTINCT  `tracked_twitter`.id FROM
(SELECT id,followers_count  FROM tracked_twitter ORDER BY followers_count DESC 
LIMIT 20) AS tracked_twitter
INNER JOIN  `twitter_content` ON  `tracked_twitter`.`id` =  `twitter_content`.`tracked_twitter_id` 
INNER JOIN  `tracker_twitter_content` ON  `twitter_content`.`id` =  `tracker_twitter_content`.`twitter_content_id` 
AND  `tracker_twitter_content`.`tracker_id` =  '88'
ORDER BY tracked_twitter.followers_count DESC 

【讨论】:

  • 这不可能是答案。因为您没有按子句顺序考虑其他表的约束。所以它只需要从父表中获取 20 条记录并与其他表连接。执行查询时我只有两行。
【解决方案3】:

主要问题是即使您的行数相对较少,您也使用varchar(255) COLLATE utf8_unicode_ci 作为主键(而不是整数),因此在其他表中作为外键。我怀疑同样的问题是twitter_content.id。这会导致大量长字符串比较,并为临时表保留大量额外内存。

关于查询本身,是的,它应该是沿着followers_count 索引并检查相关表的条件的查询。这可以按照 Gordon Linoff 的建议来完成,或者通过使用索引提示来完成。

【讨论】:

    猜你喜欢
    • 2021-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-19
    • 2012-06-16
    相关资源
    最近更新 更多