【问题标题】:Optimize an ORDER BY query优化 ORDER BY 查询
【发布时间】:2011-05-13 03:41:39
【问题描述】:

我很茫然。我有一个大约 100K 行的表。查询这张表的时候结果通常比较快,大概2ms左右。但是每当我使用 ORDER BY 时,性能就会像石头一样下降到大约 120 毫秒。我阅读了MySQL ORDER BY Optimization 页面,但我不能说我了解所有内容。尤其是索引我不清楚。

最终我想运行以下查询:

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 子句使其在 20 毫秒而不是 120 毫秒内运行。我在 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

顺便说一句,此表每周重新创建,同时不会更新。

【问题讨论】:

  • 我重新格式化查询以避免水平滚动条。最后几个查询术语(关于计划名称和可信商店)与查询的其余部分不一致(其他术语不使用表名)并且实际上不需要它们周围的括号。我争论是否在不发表评论的情况下修复它......并决定不这样做。如果您决定使这些条款保持一致,我将删除此评论。
  • 优秀的评论。部分查询已生成/已生成(更多证据表明我不太适合查询)。我删除了多余的表名。

标签: mysql optimization query-optimization sql-order-by


【解决方案1】:

您可以对 ORDER BY 子句进行多少优化是有限度的。有时有帮助的主要方法是以正确的顺序在正确的列集上建立索引。因此,对于您的示例,(单个,复合)索引:

average_price_per_month ASC, phone_price_guestimate DESC, contract_length ASC

可能会有所帮助,但优化器可能仍会决定最好使用其他索引来处理查询中的过滤条件,然后它会对自己选择的数据进行排序。请注意,除非索引以完全正确的排序顺序提供数据,并且使用索引可以整体加快查询速度,否则优化器不会使用它。仅对要排序的列之一的索引对优化器的好处有限,它通常不会使用这样的索引。

需要考虑的一个问题:

  • 不使用 ORDER BY 子句的查询执行速度有多快。

这让您可以非常直接地衡量分拣成本。您提到没有订购的 20 毫秒和订购的 120 毫秒,因此 ORDER BY 的价格适中。下一个问题可能是“您能否在您的应用程序中超越同类产品?”。您或许可以做到,但 DBMS 中的排序包通常经过了相当好的优化,您可能需要努力工作才能击败它。

【讨论】:

    【解决方案2】:

    我怀疑您的索引对您没有任何好处,因为它不是主键并且您的查询选择逻辑(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 来了解查询是如何执行的(如果我的直觉不正确)并相应地调整索引。

    【讨论】:

      猜你喜欢
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-08
      • 1970-01-01
      • 2012-06-12
      相关资源
      最近更新 更多