2010-11-20 71 views
1

我很茫然。我有一個約100K行的桌子。查詢此表時結果通常很快,大約2ms左右。但是每當我使用ORDER BY的表現會像一塊石頭一樣下降到大約120ms。我讀了MySQL ORDER BY Optimization頁面,但我不能說我明白了一切。特別是指標不清楚。優化ORDER BY查詢

最後,我想運行下面的查詢:

SELECT * 
    FROM `affiliate_new_contracts` 
WHERE phone_brand IN ('Apple','Blackberry','HTC','LG','Motorola','Nokia', 
         'Samsung','Sony Ericsson') 
    AND contract_length IN ('12','24') 
    AND (addon IS NULL OR addon IN('Telfort Sms 300','Surf & Mail')) 
    AND (plan_name = 'Telfort 100' 
     AND 
     credible_shop = 1 
     ) 
    ORDER BY average_price_per_month ASC, phone_price_guestimate DESC, 
      contract_length ASC; 

不過,我會很高興,如果我理解的基本原則。
刪除先前查詢中的ORDER BY子句使其運行時間爲20ms而不是120ms。我在average_price_per_month字段上有一個索引,但是將ORDER BY子句簡化爲ORDER BY average_price_per_month並沒有提高性能。我不明白。我也對所謂的多列索引在黑暗中應該能夠幫助我進行最終查詢。

任何幫助,將不勝感激。我如何讓這個壞男孩表演?還是那個追求烏托邦?

CREATE TABLE語法如下:

$ show create table affiliate_new_contracts; 
CREATE TABLE `affiliate_new_contracts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `contract_length` int(11) DEFAULT NULL, 
    `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `price` float DEFAULT NULL, 
    `average_price_per_month` float DEFAULT NULL, 
    `phone_price_guestimate` float DEFAULT NULL, 
    `credible_shop` tinyint(1) DEFAULT '0', 
    `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `addon_price` float DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`), 
    KEY `index_affiliate_new_contracts_on_average_price_per_month` (`average_price_per_month`), 
    KEY `index_affiliate_new_contracts_on_price` (`price`) 
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

BTW此表重新每週和在此期間未更新。

+1

我重新格式化了查詢以避免水平滾動條。最後幾個查詢條件(計劃名稱和可信商店)與查詢的其餘部分(其他條款不使用表名稱)不一致,並且實際上不需要它們周圍的括號。我辯論是否解決它沒有評論...並決定不要。如果您決定使這些條款保持一致,我會刪除此評論。 – 2010-11-20 17:53:52

+0

優秀的評論。部分查詢被生成(更多證據我很不適合查詢)。我刪除了多餘的表名。 – harm 2010-11-20 18:27:52

回答

3

ORDER BY子句可以做多少優化是有限制的。有時幫助的主要方法是按照正確的順序對正確的一組列進行索引。所以,你的例子,一個(單個複合)指數:

average_price_per_month ASC, phone_price_guestimate DESC, contract_length ASC 

可能有幫助,但優化程序可能仍然決定,它是更好地使用一些其他的指標來處理查詢的過濾條件和那麼它會對自己選擇的數據進行排序。請注意,除非索引完全按照正確的排序順序提供數據,並且使用索引可以加快整個查詢的速度,否則優化程序將不會使用它。只有一個要排序的列的索引對優化器來說是一個有限的好處,它通常不會使用這樣的索引。

一個問題需要考慮:

  • 有多快查詢,而不ORDER BY子句執行。

這給你一個非常直接的分類成本測量。您提到20毫秒沒有排序和120毫秒排序,所以ORDER BY是適度昂貴的。接下來的問題可能是「你能在應用程序中勝過它嗎?」。你也許可以做到這一點,但是DBMS中的排序包通常都進行了很好的優化,你可能必須努力工作才能打敗它。

0

我懷疑你的索引對你沒有任何好處,因爲它不是主鍵,而你的查詢選擇邏輯(where子句)不使用它。由於您沒有使用索引來選擇哪些行,最終必須在選擇後對結果進行排序。事實上,這不是你的主要關鍵,這意味着結果還沒有按每月的平均價格排序,這將減少或消除排序時間,因爲它們已經被訂購。

一種解決方案是使用包含最具選擇性的列(計劃名稱)和排序列(average_price_per_month)的複合索引。在選擇之後,它仍然需要進行排序,但結果已經由主排序列排序,從而減少了花費的時間。

CREATE TABLE `affiliate_new_contracts` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `plan_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `contract_length` int(11) DEFAULT NULL, 
    `phone_brand` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `price` float DEFAULT NULL, 
    `average_price_per_month` float DEFAULT NULL, 
    `phone_price_guestimate` float DEFAULT NULL, 
    `credible_shop` tinyint(1) DEFAULT '0', 
    `addon` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `addon_price` float DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `index_affiliate_new_contracts_on_plan_name` (`plan_name`,`average_price_per_month`), 
    KEY `index_affiliate_new_contracts_on_price` (`price`) 
) ENGINE=InnoDB AUTO_INCREMENT=2472311 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

你也可能要使用EXPLAIN,以瞭解正在執行的查詢(如果我的直覺是不正確的),並相應地調整指數。