【问题标题】:Slow MYSQL Queries, need help understanding indexesMYSQL查询慢,需要帮助理解索引
【发布时间】:2016-02-13 17:10:03
【问题描述】:

出于本文的目的,我已将我的问题简化为最纯粹的形式。 我有 3 个表:游戏、games_tags 和 games_tags_map

如果我想为每个游戏获取标签表,我会这样做:

SELECT `games_tags_map`.`game_id` as 'game_id', GROUP_CONCAT(`games_tags_map`.`tag_id`) as 'tags'
FROM `games_tags_map`
LEFT JOIN `games_tags` on `games_tags`.id = `games_tags_map`.`tag_id`
GROUP BY `games_tags_map`.game_id

大约需要 1 毫秒

SELECT `games`.`id` AS 'id' from `games`

这需要

但是,当我尝试加入这两个时:

SELECT `games`.`id` AS 'id',
t.`tags` as `tags`
FROM `games`
LEFT JOIN (
    SELECT `games_tags_map`.`game_id` as 'game_id', GROUP_CONCAT(`games_tags_map`.`tag_id`) as 'tags'
    FROM `games_tags_map`
    LEFT JOIN `games_tags` on `games_tags`.id = `games_tags_map`.`tag_id`
    GROUP BY `games_tags_map`.game_id
) t ON t.`game_id`=`games`.`id`

大约需要 100 毫秒

但是,当我执行等效查询时:

SELECT `games`.`id` AS 'id',
GROUP_CONCAT(DISTINCT `games_tags`.`tag`) AS 'tags'
FROM `games`
LEFT JOIN `games_tags_map` ON `games`.`id` = `games_tags_map`.`game_id`
LEFT JOIN `games_tags` ON `games_tags`.`id` = `games_tags_map`.`tag_id`
WHERE `games`.`active`=1
GROUP BY `games`.`id`

需要 2 毫秒... 但是,当我需要按主列 (id) 以外的任何内容对其进行排序时,大约需要 80 毫秒

澄清一下,这是我实际数据库的一个非常简化的版本,它的加载时间要长得多,并导致我的网站出现问题,但问题出在这些查询中。

我的数据库设置方式显然存在缺陷,以适应如此巨大不同的加载时间。我尝试添加更多索引,但没有帮助。 在“游戏”表上,我有主索引“id” 在表 'games_authors_map' 上,主索引由 'game_id' 和 'author_id' 组成

我知道有问题,但我无法解决它,我不明白为什么。

请帮忙。

【问题讨论】:

    标签: mysql sql mariadb


    【解决方案1】:

    与其加入所有游戏标签表(这本身没问题),不如在主游戏表中添加一个聚合列,将所有标签放在前面,这样你就不需要加入了。然后,您可以简单地添加一个触发器,每当从 game_tags_map 表中添加或删除标签时,它都会更新主游戏表。如果这只是为了显示到基于 Web 的游戏站点,那么您很好。如果一个人对某种类型的游戏感兴趣,那么对 game_tags_map 表的查询会很好地总结出该特定兴趣的列表。

    您还每次都在查询所有游戏,所以这可能是您更好的途径。

    首先,查看您的第一个查询,并删除勾选, 将您的长表名分别引用和别名为 gtm 和 gt, 您的查询甚至从不使用 games_tags 表,因为它是左连接 并且不使用其中的任何列...

    SELECT 
          gtm.game_id, 
          GROUP_CONCAT(gtm.tag_id) as tags
       FROM 
          games_tags_map gtm
             LEFT JOIN games_tags gt 
                on gtm.tag_id = gt.id
       GROUP BY 
          gtm.game_id
    

    所以本质上,它只不过是在做

    SELECT 
          gtm.game_id, 
          GROUP_CONCAT(gtm.tag_id) as tags
       FROM 
          games_tags_map gtm
       GROUP BY 
          gtm.game_id
    

    除非您打算使用 group_concat() 来显示文字描述 所代表的 ID 而不是 ID 本身。如果按 ID,则 您的第二个查询还可以删除 games_tags 表的内部左连接。

    SELECT 
          g.id AS id,
          t.tags as tags
       FROM 
          games g
             LEFT JOIN ( SELECT 
                              gtm.game_id, 
                              GROUP_CONCAT(gtm.tag_id) as tags
                           FROM 
                              games_tags_map gtm
                                 LEFT JOIN games_tags 
                                    on gtm.tag_id = gt.id
                           GROUP BY gtm.game_id ) t 
                ON g.id = t.game_id
    

    在您的最后一个查询中,您是左连接以实际获取标签说明 而不是标签。

    SELECT 
          g.id,
          GROUP_CONCAT(DISTINCT gt.tag) AS tags
       FROM 
          games g
             LEFT JOIN games_tags_map gtm 
                ON g.id = gtm.game_id
                LEFT JOIN games_tags gt
                   ON gtm.tag_id = gt.id 
       WHERE 
          g.active = 1
       GROUP BY 
          g.id
    

    为了优化这个查询,我会提供以下索引..
    这将使整个查询与覆盖索引一起使用并且可以处理 通过索引进行整个查询,并且永远不需要转到原始基础数据。

    table           index
    games           ( active, id )
    games_tags_map  ( game_id, tag_id )
    games_tags      ( id, tag )
    

    最后一点,在尝试为帖子提供更多详细信息时,您可以 始终编​​辑您现有的帖子,添加更多详细信息,然后向用户发送评论 关于提供的额外数据以审查并可能提供额外的 内容/答案/响应。

    【讨论】:

    • 出于其他原因,我正在考虑这条路线,但问题仍然存在,为什么 2 个连接查询明显慢于应有的速度?我相信一定有办法通过篡改表结构来优化它们。
    • @hedgehog90,请参阅修订发布以澄清您的查询和涵盖索引。
    • 感谢 DRapp,非常有帮助的帖子。我最终做的是在游戏表上创建几个新列,例如“_tags”(我放了一个下划线前缀来提醒我这是一个自动字段),每次我向 gt 或 gtm 添加一个新标签时,它也使用所有标签文字设置此列。我以这种方式避免了许多连接和分组,它在性能上产生了巨大的差异。现在我只需要在过滤具有特定标签的游戏​​或对游戏之间的相似度(使用它们的标签和其他数据)进行排名时参考 gt 和 gtm 表
    【解决方案2】:

    尝试对表中的外键使用索引(games_tags_map.tag_idgames_tags_map.game_id)并同时索引您尝试对查询进行排序的列 这将解决您的问题。

    【讨论】:

    • 还在 game.active 上添加索引。您可以运行 EXLAIN yourQuer 看看这是否有效。在结果中,您可以看到它是如何工作的。如果您使用更多信息发布查询结果
    • 我尝试将 game_id 和 tag_id 添加为单独的索引。当我尝试两个版本的查询时没有改善。我们可以专注于我提到的第一个查询耗时约 100 毫秒吗?它没有'order by',为什么这么慢,我该如何改进它?
    • 第一个查询不是正确的方法,因为您在连接中使用子查询会运行缓慢,那么为什么您没有使用连续连接而使用子查询呢?此外,如果第二个查询为您提供预期的结果,那么向您要排序的字段添加索引将解决您的问题。
    • 好的,考虑到第二个查询,我尝试将排序字段添加为索引,但没有帮助。
    • 能否请您给出运行的输出解释您的查询?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多