2015-02-24 67 views
1

我在表上運行follwing查詢,我正在更改where條件中的值,而在一個情況下運行時,它正在採取一個索引,另一個案例採取另一個(錯誤? )指數。MySql沒有爲幾個查詢選擇正確的索引

行數查詢1是402954它採取約1.5秒

行數查詢2是52097它採取大約35秒

兩個查詢查詢1和查詢2是相同的,只是我在其中狀態變更值

查詢1

EXPLAIN SELECT 
    log_type,count(DISTINCT subscriber_id) AS distinct_count, 
    count(subscriber_id) as total_count 
FROM campaign_logs 
WHERE 
    domain = 'xxx' AND 
    campaign_id='123' AND 
    log_type IN ('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED') AND 
    log_time BETWEEN 
     CONVERT_TZ('2015-02-12 00:00:00','+05:30','+00:00') AND 
     CONVERT_TZ('2015-02-19 23:59:58','+05:30','+00:00') 
GROUP BY log_type; 

EXPLAIN上述查詢

的3210
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+ 
| id | select_type | table   | type | possible_keys                      | key          | key_len | ref | rows | Extra  | 
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+ 
| 1 | SIMPLE  | campaign_logs | range | campaign_id_index,domain_index,log_type_index,log_time_index,campaignid_domain_logtype_logtime_index | campaignid_domain_logtype_logtime_index | 468  | NULL | 402954 | Using where | 
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+ 

查詢上述查詢的2

EXPLAIN SELECT 
    log_type,count(DISTINCT subscriber_id) AS distinct_count, 
    count(subscriber_id) as total_count 
FROM stats.campaign_logs 
WHERE 
    domain = 'yyy' AND 
    campaign_id='345' AND 
    log_type IN ('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED') AND 
    log_time BETWEEN 
     CONVERT_TZ('2014-02-05 00:00:00','+05:30','+00:00') AND 
     CONVERT_TZ('2015-02-19 23:59:58','+05:30','+00:00') 
GROUP BY log_type; 

解釋

+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+ 
| id | select_type | table   | type  | possible_keys                      | key       | key_len | ref | rows | Extra                  | 
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+ 
| 1 | SIMPLE  | campaign_logs | index_merge | campaign_id_index,domain_index,log_type_index,log_time_index,campaignid_domain_logtype_logtime_index | campaign_id_index,domain_index | 153,153 | NULL | 52097 | Using intersect(campaign_id_index,domain_index); Using where; Using filesort | 
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+ 

查詢1是使用正確的索引,因爲我有綜合指數

查詢2使用的是指數的合併,它採取長時間執行

爲什麼選擇MySq l對同一查詢使用不同的索引

我知道我們可以在查詢中提及USE INDEX,但是爲什麼MySql在這種情況下沒有選擇正確的索引??。我做錯了什麼?

+1

有時壞查詢優化是關係到壞的統計數據 - 有很多,可以去on和MySQL在此處將其記錄在此處:http://dev.mysql.com/doc/innodb/1.1/en/innodb-other-changes-statistics-estimation.html。考慮運行ANALYZE TABLE來更新有關索引分佈的統計信息,然後重新運行解釋。 – Chipmonkey 2015-02-24 14:30:17

回答

1

我建議你試試這個:

添加你的複合索引的排列。

(campaign_id,domain,log_time,log_type,subscriber_id) 

更改您的查詢,刪除WHERE log_type IN()標準,從而使聚合函數使用它找到的所有記錄的範圍掃描上log_time。在索引中包括subscriber_id應允許從索引直接滿足整個查詢。也就是說,這是一個覆蓋索引的

最後,你可以在你的log_type值通過包裹整個查詢中

SELECT * 
    FROM (/*the whole query*/) x 
    WHERE log_type IN 
     ('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED') 
    ORDER BY log_type 

此過濾器應該給你更好的,更可預測的,性能。

(除非你想的log_types是記錄一個微小的子集,在這種情況下,請忽略此建議。)這樣

+0

這是真的。 MySQL只能使用索引搜索到第一個範圍。刪除log_type的範圍要求應該可以提高性能,但是您稍後可能會在外部查詢中添加混合結果。如果你這樣做,你還必須從*覆蓋*索引中刪除log_type。 – 2015-02-24 16:59:55

3

不,你沒有做錯任何事。由於Chipmonkey在評論中指出,有時MySQL會因爲過時的表統計數據而選擇錯誤的執行計劃。您可以通過執行ANALYZE TABLE來更新表格統計信息。

儘管如此,MySQL優化器並不那麼複雜。它看到,在這兩種情況下,MySQL都必須訪問二級索引,然後查找聚簇索引以獲取實際的表數據,因此,當看到可能第二個查詢通過使用兩個單獨索引具有更好的選擇性並且合併它們,你不能因爲猜測錯誤而責怪它太多。

我猜如果你有一個覆蓋索引,以便MySQL可以用索引執行整個查詢,那麼它將有利於該索引執行合併。

嘗試將subscriber_id添加到多列索引的末尾以獲得覆蓋索引。

否則,請使用USE INDEXFORCE INDEX,因爲這就是它們的用途。你比MySQL更瞭解數據。

+0

@亞當斯,感謝您的評論,該表有2500萬+記錄,它有8列,所以我的問題是安全的覆蓋5列的索引,我懷疑它會增加磁盤空間。 – Rams 2015-02-24 14:50:33

+0

是的,它會增加磁盤空間(理想的內存佔用空間)。什麼是更多專欄? – 2015-02-24 14:55:16

+0

這裏還有一列是subscriber_id – Rams 2015-02-24 15:00:37