2016-02-13 113 views
2

爲了本文的目的,我將問題簡化爲最純粹的形式。 我有3個表:遊戲,games_tags和games_tags_map緩慢的MYSQL查詢,需要幫助理解索引

這就是我做,如果我想獲得的標籤表中每場比賽:

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` 

這需要〜100ms的

然而,當我做了等效查詢:

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

只是爲了澄清,這是我的實際數據庫的一個非常簡化的版本,它正在經歷更長的加載時間並導致我的網站出現問題,但問題在於這些查詢。

我的數據庫的設置方式顯然存在一個缺陷,因爲這樣的加載時間差別很大。我嘗試添加更多索引,但它沒有幫助。 桌子上「遊戲」我的主索引「身份證」 在表「games_authors_map」由「game_id」和「AUTHOR_ID」

的,我知道有問題,但我不能修復它的主要指標我不明白爲什麼。

請幫忙。

回答

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) 

最後一個音符,試圖提供更詳細的職位時,你可以 隨時修改現有的崗位,增加更多的細節,然後發送留言給用戶 關於提供給審查,並可能提供額外的數據額外的 內容/答案/迴應。

+0

由於其他原因,我正在考慮這條路線,但問題仍然存在,爲什麼2個連接查詢的速度明顯比他們應該慢?我確定必須有辦法通過篡改表結構來優化它們。 – hedgehog90

+0

@ hedgehog90,請參閱修訂過帳以澄清您的查詢並涵蓋索引。 – DRapp

+0

感謝DRapp,非常有幫助的帖子。我最終做的是在遊戲桌上創建幾個新列,比如'_tags'(我把一個下劃線前綴提醒我這是一個自動化領域),每次我給gt或gt添加一個新標籤時,它也是使用所有標籤文字設置此列。我避免了許多加入和分組,並且它在性能上造成了巨大的差異。現在我只需要在使用特定標籤過濾遊戲時或者在排列遊戲之間的相似性(使用它們的標籤和其他數據)時參考gt和gtm表格, – hedgehog90

0

嘗試使用外鍵索引的表(games_tags_maptag_idgames_tags_mapgame_id)和指數也列從中你想查詢 這將解決您的問題排序]。

+0

也增加了game.active的索引。你可以看看這個工程是否運行EXLAIN yourQuer。結果你可以看到它是如何工作的。如果您使用更多的信息發佈您的查詢 –

+0

的結果,我嘗試將game_id和tag_id添加爲單獨的索引。當我嘗試兩個版本的查詢時沒有改進。我們可以專注於我提到的第一個查詢花費了大約100ms嗎?它沒有'排序',爲什麼這麼慢,我該如何改進? – hedgehog90

+0

第一個查詢不是正確的方法,因爲您在聯接中使用子查詢的速度會很慢,所以爲什麼您沒有使用連續聯接並使用子查詢呢? 也如果第二個查詢給你預期的結果,然後將索引添加到您想要排序的字段將解決您的問題。 –