慢MYSQL查询,需要帮助理解索引

时间:2016-02-13 17:10:04

标签: mysql sql mariadb

就本文而言,我已将问题简化为最纯粹的形式。 我有3个表:games,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

需要~1ms

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

这需要< 1ms。

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

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`

需要2ms ...... 但是,当我需要通过主列(id)之外的任何其他命令时,它需要~80ms

只是为了澄清,这是我的实际数据库的一个非常简单的版本,它经历了更长的加载时间并导致我的网站出现问题,但问题出现在这些查询中。

我的数据库设置方式存在明显的缺陷,因为加载时间非常不同。我尝试过添加更多索引,但它没有帮助。 桌上游戏'我有主要索引' id' 在桌子上#games_authors_map'主要索引包含' game_id'和' author_id'

我知道有问题,但我无法修复,而且我不明白为什么。

请帮忙。

2 个答案:

答案 0 :(得分:2)

为什么不在主游戏桌面上添加聚合列,而不是连接所有游戏标签表(这本身就可以),所以你不需要加入。然后,您可以简单地添加一个触发器,无论何时在game_tags_map表中添加或删除标记,它都会更新主游戏表。如果这只是为了显示到基于网络的游戏网站,你就是好的。如果某人对某种类型的游戏感兴趣,那么针对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 )

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

答案 1 :(得分:0)

尝试在表格中使用外键索引(games_tags_maptag_idgames_tags_mapgame_id),同时索引您尝试使用的列对查询进行排序 这将解决您的问题。