2010-11-20 73 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,以了解正在执行的查询(如果我的直觉是不正确的),并相应地调整指数。