5

如果我有一个类似于下面的语句的 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:有人要求下面的解释结果。让我知道是否需要其他任何东西

在此处输入图像描述


编辑#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);
4

2 回答 2

1

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

规则是,如果您只使用复合索引的一部分,则使用的部分应该是前导部分。

其次,Group By 应该匹配一个索引。

如果您可以通过某种方式使您的查询使用“=”而不是“>”,那么您的性能会更好,这是一个范围检查查询。

于 2012-07-23T17:23:52.590 回答
0

主要问题是 MySQL 不能直接使用索引来处理聚合。这是由于 join withMyCurrencyPairs以及您要求的MIN(BenchmarkId)同时还具有范围条件 on的事实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)提示,即使是“固定值”列,Currency1并且Currency2出现BenchmarkTypeORDER-part 中,也是使 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.07 秒对 0.05 秒),但我的基准表中只有大约 120 万行,数据分布可能很差,所以 YMMV。

于 2012-07-24T21:18:37.630 回答