2016-02-04 39 views
0

我有一個簡單的MySQL 5.6.23 GROUP BY查詢,它需要32秒才能在RDS db.r3.xlarge instance上運行。 InnoDB表格大約有47M行。 explain說我選擇了大約8K。最終的GROUP BY輸出有86行。根據show processlist; 99%的時間花在Creating sort index上。如果我大大增加menu_id in (...)列表中的ids數量,查詢需要10-30分鐘。優化困在「創建排序索引」的簡單MySQL GROUP BY查詢

不幸的是,我無法從數據庫服務器複製/粘貼文本到這個終端,所以下面的表格輸出是縮寫的。

查詢信息:

SELECT COUNT(DISTINCT user_id) AS count_user_id, org, category 
    FROM menu_views 
    WHERE menu_id in (
    ...about 1300 ids... 
) GROUP BY org, category; 

explain; 
| id | select_type | table  | type | possible_keys                   | key     | key_len | ref | rows | Extra         | 
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 
| 1 | SIMPLE  | menu_views | range | i_menu_view_menu_id,tyler_group,tyler_user_group,tyler_user_menu_group,tyler_menu_group | i_menu_views_menu_id | 5  | NULL | 7914 | Using index condition; Using filesort | 

輸出:

| count_user_id | org | category | 
|--------------------------------| 
| 13000   | foo | pizza | 
| 1    | bar | candy | 
| 90   | baz | cheese | 
| 80   | gaz | soda  | 
| 150   | urk | pizza | 
|  ... etc (86 rows) ...  | 
|--------------------------------| 

背景信息:

describe menu_views; 

| Field | Type   | Null | Key | Default | 
|------------------------------------------------| 
| id  | int(11)  | NO | PRI | NULL | 
| menu_id | int(11)  | YES | MUL | NULL | 
| user_id | int(11)  | YES | MUL | NULL | 
| category | varchar(255) | NO |  | UNKNOWN | 
| org  | varchar(255) | NO | MUL | UNKNOWN | 
|------------------------------------------------| 

show index from menu_views; 

| Key_name    | Seq_in_index | Column_name | 
|-----------------------------------------------------| 
| PRIMARY    | 1   | id   | 
| i_menu_views_menu_id | 1   | menu_id  | 
| tyler_group   | 1   | org   | 
| tyler_group   | 2   | category | 
| tyler_user_group  | 1   | user_id  | 
| tyler_user_group  | 2   | org   | 
| tyler_user_group  | 3   | category | 
| tyler_user_menu_group | 1   | user_id  | 
| tyler_user_menu_group | 2   | menu_id  | 
| tyler_user_menu_group | 3   | org   | 
| tyler_user_menu_group | 4   | category | 
| tyler_menu_group  | 1   | menu_id  | 
| tyler_menu_group  | 2   | org   | 
| tyler_menu_group  | 3   | category | 
|-----------------------------------------------------| 

有AR e表中的其他索引,但這些是通過EXPLAIN顯示的索引。我添加了tyler_*,試圖強制使用loose index scan,但沒有幫助。

orgcategory字段正確屬於users,但我非規範化它們,希望非JOIN查詢會更快。但是,我沒有看到任何性能改進。

完全披露:我使用此查詢的幾個變體,所有這些變化都很慢。這是最簡單的變體。其他包括WHERE created_at BETWEEN ('X' and 'Y')GROUP BY year/month/week/day(created_at), category

回答

0

我似乎終於在正確的軌道上,在盯着很多其他人的代碼和博客文章。我意識到我永遠不會達到loose index scan,因爲我使用COUNTGROUP BY。原來真的很慢的部分是COUNT(DISTINCT user_id)。我可以運行與COUNT(user_id)完全相同的查詢並在兩秒內收到結果。快得多,但我的目的是錯誤的數據。

我現在的優化版本,使用子查詢,方法是:

SELECT COUNT(user_id) AS count_user_id, org, category FROM (
    SELECT user_id, org, category 
    FROM menu_views 
    WHERE menu_id IN (
    ... lots of ids ... 
) GROUP BY user_id, org, category 
) AS groupings 
GROUP BY org, category; 

我想我還是需要使用索引和喜歡玩,但運行在原來的查詢的20%的時間。

0

給這些一試:

INDEX(user_id, org, category) -- covering index for either of your queries. 
INDEX(created_at, category) -- for the additional example 

輸出意味着它不得不接觸超過13K行。通過上述索引,它可以完成索引中的所有工作,而無需涉及數據。

(請提供SHOW CREATE TABLE,它比DESCRIBE更具描述性。)

47M行,你應該考慮'正常化'orgcategory - 我認爲這些領域有很多重複?我無法分辨我們的查詢是否受I/O限制,但這會降低此類可能性。