慢 MYSQL 查询,需要帮助理解索引
Slow MYSQL Queries, need help understanding indexes
出于此 post 的目的,我已将我的问题简化为最纯粹的形式。
我有 3 个 table:游戏,games_tags 和 games_tags_map
如果我想为每个游戏获取 table 个标签,我会这样做:
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`
这需要 <1 毫秒。
然而,当我尝试加入这两个时:
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) 以外的任何其他方式对其进行排序时,需要 ~80ms
澄清一下,这是我实际数据库的一个非常简化的版本,它的加载时间要长得多,并导致我的网站出现问题,但问题出在这些查询中。
我的数据库针对如此巨大不同的加载时间设置的方式显然存在缺陷。我试过添加更多索引,但没有帮助。
在 table 'games' 我有主索引 'id'
在 table 'games_authors_map' 上,由 'game_id' 和 'author_id'
组成的主索引
我知道有问题,但我无法解决,我不明白为什么。
请帮忙。
尝试对表中的外键使用索引(games_tags_map
.tag_id
,games_tags_map
.game_id
)并且还索引您从中获取的列试图对查询进行排序
这将解决您的问题。
与其对所有游戏标签 table 进行连接(这本身就可以),为什么不在主游戏 table 中添加一个聚合列,所有标签都放在前面,这样你不需要加入。然后,您可以简单地添加一个触发器,每当从 game_tags_map table 添加或删除标签时,它都会更新主游戏 table。如果这只是为了显示给基于网络的游戏站点,那很好。如果某人对某种类型的游戏感兴趣,那么针对 game_tags_map table 的查询将很好地总结出该特定兴趣的列表。
您每次都在查询所有游戏,所以这可能是您更好的途径。
首先,查看您的第一个查询并删除勾号,
将您的长 table 名称分别引号和别名为 gtm 和 gt,
您的查询甚至从未使用 games_tags table 因为它是左连接
并且不使用其中的任何列...
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 table.
的内部左连接
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 )
最后一点,在尝试向 post 提供更多详细信息时,您可以
始终编辑您现有的 post,添加更多详细信息,然后向用户发送评论
关于提供的额外数据以供审查并可能提供额外的
内容/回答/回应。
出于此 post 的目的,我已将我的问题简化为最纯粹的形式。 我有 3 个 table:游戏,games_tags 和 games_tags_map
如果我想为每个游戏获取 table 个标签,我会这样做:
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`
这需要 <1 毫秒。
然而,当我尝试加入这两个时:
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) 以外的任何其他方式对其进行排序时,需要 ~80ms
澄清一下,这是我实际数据库的一个非常简化的版本,它的加载时间要长得多,并导致我的网站出现问题,但问题出在这些查询中。
我的数据库针对如此巨大不同的加载时间设置的方式显然存在缺陷。我试过添加更多索引,但没有帮助。 在 table 'games' 我有主索引 'id' 在 table 'games_authors_map' 上,由 'game_id' 和 'author_id'
组成的主索引我知道有问题,但我无法解决,我不明白为什么。
请帮忙。
尝试对表中的外键使用索引(games_tags_map
.tag_id
,games_tags_map
.game_id
)并且还索引您从中获取的列试图对查询进行排序
这将解决您的问题。
与其对所有游戏标签 table 进行连接(这本身就可以),为什么不在主游戏 table 中添加一个聚合列,所有标签都放在前面,这样你不需要加入。然后,您可以简单地添加一个触发器,每当从 game_tags_map table 添加或删除标签时,它都会更新主游戏 table。如果这只是为了显示给基于网络的游戏站点,那很好。如果某人对某种类型的游戏感兴趣,那么针对 game_tags_map table 的查询将很好地总结出该特定兴趣的列表。
您每次都在查询所有游戏,所以这可能是您更好的途径。
首先,查看您的第一个查询并删除勾号, 将您的长 table 名称分别引号和别名为 gtm 和 gt, 您的查询甚至从未使用 games_tags table 因为它是左连接 并且不使用其中的任何列...
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 table.
的内部左连接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 )
最后一点,在尝试向 post 提供更多详细信息时,您可以 始终编辑您现有的 post,添加更多详细信息,然后向用户发送评论 关于提供的额外数据以供审查并可能提供额外的 内容/回答/回应。