2013-01-16 60 views
0

我有一個大型的表(250M行),其中列group_id大致將表分成組(group_id)。它具有以下指標:錯誤的mysql索引選擇

mysql> show indexes from table\G; 
*************************** 13. row *************************** 
     Table: table 
    Non_unique: 1 
    Key_name: myindex 
Seq_in_index: 1 
Column_name: group_id 
    Collation: A 
Cardinality: 181819 
    Sub_part: NULL 
     Packed: NULL 
     Null: YES 
    Index_type: BTREE 
    Comment: 
*************************** 14. row *************************** 
     Table: table 
    Non_unique: 1 
    Key_name: myindex 
Seq_in_index: 2 
Column_name: id 
    Collation: A 
Cardinality: 213456239 
    Sub_part: NULL 
     Packed: NULL 
     Null: 
    Index_type: BTREE 
    Comment: 

我想執行以下查詢:

mysql> explain select * from `table` WHERE (`table`.`type_id` IN (11, 17, 12, 19) AND `table`.`group_id` = 310248) ORDER BY `table`.`id` ASC LIMIT 201\G 

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: table 
     type: index 
possible_keys: [SOME INDEX NAMES] 
      key: PRIMARY 
     key_len: 4 
      ref: NULL 
     rows: 257386914 
     Extra: Using where 
1 row in set (0.00 sec) 

據我所知,這將需要掃描因爲與索引的問題,對於那些......在某些行()。然而,令人驚訝的是,它選擇通過使用主鍵索引掃描儘可能多的行。

下面似乎明確(顯然)優於:

mysql> explain select * from `table` USE INDEX (myindex) WHERE (`table`.`type_id` IN (11, 17, 12, 19) AND `table`.`group_id` = 310248) ORDER BY `table`.`id` ASC LIMIT 201\G 
*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: table 
     type: ref 
possible_keys: myindex 
      key: myindex 
     key_len: 5 
      ref: const 
     rows: 1883760 
     Extra: Using where 
1 row in set (0.00 sec) 

使用對LIMIT(2000)大的值,使用GROUP_ID的不同的值,通過除去ORDER併除去TYPE_ID濾波器這一切導致使用索引。我已經運行ANALYZE TABLE。

其值得注意的是,該行的估計是相當高:

mysql> select count(*) from table where group_id=310248 and type_id in (11, 17, 12, 19) ; 
+----------+ 
| count(*) | 
+----------+ 
| 583868 | 
+----------+ 
1 row in set (0.61 sec) 

MySQL版本:

版57年1月5日 - rel12.8日誌用於在x86_64 Debian的Linux-GNU((Percona的服務器(GPL),12.8,修訂版233))

爲什麼mysql會選擇一個它認爲會掃描257386914行而不是1883760的計劃?我知道它可能會重複順序讀取,但爲什麼它會選擇2000行的索引,而不是200行?爲什麼要用不同的組ID進行過濾?

編輯:我也嘗試創建索引(group_id,id,type_id),以便所有的排序只能使用索引掃描來完成,但我無法讓它選擇那個索引。

回答

1

您有問題嗎?

請注意,因爲type_id列上的謂詞必須被檢查,並且由於您的查詢返回了至少一個不在索引中的列,所以MySQL將不得不訪問表的數據頁,以便訪問這些列的值。

因此,MySQL可能會傾向於使用集羣密鑰,因爲這是數據頁面所在的位置;集羣密鑰還允許MySQL避免排序操作(「使用filesort」)。 (我們注意到,使用索引的執行計劃也避免了排序操作。)

如果您希望MySQL支持您的索引,那麼可以考慮在該索引中包含type_id作爲第三列,如果這樣可選擇的。

或者,您可能會考慮將您的查詢修改爲「ORDER BY group_id,id」以影響優化程序。

您是否使用提示和提示測量了查詢的性能?

+0

對不起,你說得對,我添加了一個我有問題的樣本。正如我在編輯中注意到的,我曾嘗試在索引中添加type_id。有了hist,我可以在取消前大約10分鐘,並且提示時間<2秒。 – jaklaassen