索引列和订单

时间:2012-07-23 16:02:42

标签: mysql

如果我有一个像下面的语句那样的select语句,那么索引中应该包含哪些顺序和哪些列?

SELECT MIN(BenchmarkID),
       MIN(BenchmarkDateTime),
       Currency1,
       Currency2,
       BenchmarkType
FROM Benchmark
       INNER JOIN MyCurrencyPairs ON Currency1 = Pair1
                            AND Currency2 = Pair2
WHERE BenchmarkDateTime > IN_BeginningTime
GROUP BY Currency1, Currency2, BenchmarkType;

需要注意的事项:

  • 基准表将有数十亿行
  • MyCurrencyPairs表是一个本地表,其记录少于10条
  • IN_BeginningTime是输入参数
  • 列Currency1和Currency2是VARCHAR
  • 列BenchmarkID和BenchmarkType为INT
  • 列BenchmarkDateTime是一个日期时间(希望很明显)

我已经使用Currency1,Currency2,BenchmarkType,BenchmarkDateTime和BenchmarkID创建了一个索引,但我没有达到我想要的速度。我可以创建一个更好的索引吗?


编辑#1:有人要求下面的解释结果。如果还有其他需要,请告诉我

enter image description here


编辑#2:有人请求两个表的DDL(我假设这是创建语句):

(此基准表存在于数据库中)

CREATE TABLE `benchmark` (
    `SequenceNumber` INT(11) NOT NULL,
    `BenchmarkType` TINYINT(3) UNSIGNED NOT NULL,
    `BenchmarkDateTime` DATETIME NOT NULL,
    `Identifier` CHAR(6) NOT NULL,
    `Currency1` CHAR(3) NULL DEFAULT NULL,
    `Currency2` CHAR(3) NULL DEFAULT NULL,
    `AvgBMBid` DECIMAL(18,9) NOT NULL,
    `AvgBMOffer` DECIMAL(18,9) NOT NULL,
    `AvgBMMid` DECIMAL(18,9) NOT NULL,
    `MedianBMBid` DECIMAL(18,9) NOT NULL,
    `MedianBMOffer` DECIMAL(18,9) NOT NULL,
    `OpenBMBid` DECIMAL(18,9) NOT NULL,
    `ClosingBMBid` DECIMAL(18,9) NOT NULL,
    `ClosingBMOffer` DECIMAL(18,9) NOT NULL,
    `ClosingBMMid` DECIMAL(18,9) NOT NULL,
    `LowBMBid` DECIMAL(18,9) NOT NULL,
    `HighBMOffer` DECIMAL(18,9) NOT NULL,
    `BMRange` DECIMAL(18,9) NOT NULL,
    `BenchmarkId` INT(11) NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (`BenchmarkId`),
    INDEX `NextBenchmarkIndex01` (`Currency1`, `Currency2`, `BenchmarkType`),
    INDEX `NextBenchmarkIndex02` (`BenchmarkDateTime`, `Currency1`, `Currency2`, `BenchmarkType`, `BenchmarkId`),
    INDEX `BenchmarkOptimization` (`BenchmarkType`, `BenchmarkDateTime`, `Currency1`, `Currency2`)
)

(我正在我的例行程序中创建MyCurrencyPairs表)

CREATE TEMPORARY TABLE MyCurrencyPairs
    (
        Pair1 VARCHAR(50),
        Pair2 VARCHAR(50)
    ) ENGINE=memory;
  CREATE INDEX IDX_MyCurrencyPairs ON MyCurrencyPairs (Pair1, Pair2);

2 个答案:

答案 0 :(得分:1)

BenchMarkDateTime应该是索引中的第一列。

规则是,如果仅使用复合索引的一部分,则使用的部分应该是主要部分。

其次,Group By应匹配索引。

如果您可以使用“=”代替“>”进行查询,那么您的效果会更好这是一个范围检查查询。

答案 1 :(得分:0)

主要问题是MySQL无法直接使用索引来处理聚合。这是因为与MyCurrencyPairs的加入以及您要求MIN(BenchmarkId)同时在BenchmarkDateTime上具有范围条件的事实。需要消除这两个以获得更好的执行计划。

让我们先看一下所需的索引和结果查询:

ALTER TABLE benchmark
  ADD KEY `IDX1` (
    `Currency1`,
    `Currency2`,
    `BenchmarkType`,
    `BenchmarkDateTime`
  ),
  ADD KEY `IDX2` (
    `Currency1`,
    `Currency2`,
    `BenchmarkType`,
    `BenchmarkId`,
    `BenchmarkDateTime`
  );

SELECT
  (
    SELECT
      BenchmarkId
    FROM
      benchmark FORCE KEY (IDX2)
    WHERE
      Currency1 = ob.Currency1 AND
      Currency2 = ob.Currency2 AND
      BenchmarkType = ob.BenchmarkType
      AND BenchmarkDateTime > IN_BeginningTime
    ORDER BY
      Currency1, Currency2, BenchmarkType, BenchmarkId
    LIMIT 1
  ) AS BenchmarkId
  ob.*
FROM
  (
    SELECT
      MIN(BenchmarkDateTime),
      Currency1,
      Currency2,
      BenchmarkType
    FROM
      benchmark
    WHERE
      BenchmarkDateTime > IN_BeginningTime
    GROUP BY
      Currency1, Currency2, BenchmarkType
  ) AS ob
INNER JOIN
  MyCurrencyPairs ON Currency1 = Pair1 AND Currency2 = Pair2;

第一个变化是GROUP BY部分发生在它自己的子查询中。这意味着它会生成Currency1, Currency2, BenchmarkType的所有组合,甚至是那些没有出现在MyCurrencyPairs中的组合,但除非有很多组合,否则MySQL现在可以使用索引来执行操作让这更快。此子查询使用IDX1,无需临时表或文件存储。

第二个变化是将MIN(BenchmarkId)部分隔离到它自己的子查询中。可以使用IDX2处理该子查询中的排序,因此这里也不需要排序。 FORCE KEY (IDX2)提示,即使是“固定值”列Currency1Currency2BenchmarkType出现在ORDER - 部分也需要制作MySQL优化器做正确的事。再次,这是一个权衡。如果最终结果集很大,子查询可能会失败,但我认为没有那么多行。

解释该查询提供了以下查询计划(为了便于阅读而删除了不感兴趣的列):

+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
| id | select_type        | table           | type  | key_len | rows | Extra                                 |
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
|  1 | PRIMARY            | <derived3>      | ALL   | NULL    | 1809 |                                       |
|  1 | PRIMARY            | MyCurrencyPairs | ref   | 106     |    2 | Using where                           |
|  3 | DERIVED            | benchmark       | range | 17      | 1225 | Using where; Using index for group-by |
|  2 | DEPENDENT SUBQUERY | benchmark       | ref   | 9       |  520 | Using where; Using index              |
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+

我们看到所有有趣的部分都被索引正确覆盖,我们既不需要临时表也不需要文件集。

我测试数据上的时间显示这个版本大约快20倍(1.07s对0.05s),但我的基准测试表中只有大约120万行,数据分布很可能没有,所以YMMV