2012-07-23 180 views
5

如果我有像下面的语句那样的选择语句,索引中应该包括什么顺序和哪些列?索引列和顺序

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语句)的两个表:

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

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); 
+0

你可以运行一个EXPLAIN 并发布结果。 – FreudianSlip 2012-07-23 16:36:30

+0

感谢您的解释。 Currency1和2字段,它们是varchar的原因?即包含文字?如果是这样,有什么办法可以将这些转换为查找,所以字段类型可以更改为INT?即“GBP”=> 1,“USD”=> 2 – FreudianSlip 2012-07-23 16:55:59

+0

可悲的是......这是一个已经有一段时间了,这将是一个重大变化。我希望最初的架构师已经这样做了......不知道为什么你会尝试在VARCHARs上匹配所有的东西..... – Miles 2012-07-23 17:08:03

回答

1

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

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

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

如果一些如何使您的查询使用“=”而不是作为范围检查查询的“>”,那么您的性能会更好。

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)提示和即使是“固定值”列Currency1,Currency2BenchmarkType出现在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    | 
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+ 

我们看到,所有有趣的部分是正确的索引覆盖,我们既不需要临时表,也不filesorts。

根据我的测试数据显示,这个版本的速度大约是其速度的20倍(1.07s vs. 0.05s),但我的基准表中只有大约120万行,数据分布很可能会失败,所以因人而异。

+0

即使这不适合你,我仍然很感激我们无论如何都在谈论什么时候。我想这不是在1秒范围内,但我们还在说几秒钟,还是几分钟甚至几小时? – 2012-07-24 21:27:46