我有一张桌子
CREATE TABLE price(
product_id int,
category_id int,
epoch_id int,
name varchar,
price decimal(10),
add constraint primary key (product_id, category_id, epoch_id)
);
我要选择类别中所有产品的所有价格,但要考虑所有时期:
SELECT * FROM prices where category_id = 1 ORDER BY product_id, category_id, epoch_id;
但是,我担心ORDER BY
将无法使用主键,并且会占用过多的资源来对行进行排序(正如我指定的category_id = 1
一样,它位于索引的第二位) )
我不想更改索引中的列顺序或创建新列。我想了解一下,MySQL是否将能够使用聚簇索引来快速执行排序。
更新: 我已经产生了大约100,000行,这就是我的结果:
explain SELECT * FROM price where category_id = 1 ORDER BY category_id, product_id, epoch_id;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE price index PRIMARY 12 97739 10 Using where
explain SELECT * FROM price where category_id = 1 ORDER BY category_id, epoch_id;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE price ALL 97739 10 Using where; Using filesort
explain SELECT * FROM price where category_id = 1 ORDER BY category_id, epoch_id, product_id;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE price ALL 97739 10 Using where; Using filesort
explain SELECT * FROM price where category_id = 1 ORDER BY product_id, epoch_id, category_id;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE price index PRIMARY 12 97739 10 Using where
explain SELECT * FROM price where category_id = 1 ORDER BY product_id, epoch_id;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE price index PRIMARY 12 97739 10 Using where
所以现在我有几个问题:
为什么product_id, epoch_id, category_id
不使用文件排序,尽管顺序与PK顺序相矛盾? -是因为category_id
受WHERE
子句限制,并且product, epoch
的顺序保留在PK中吗?
为什么product_id, epoch_id
不需要文件排序,但是category_id, epoch_id
却需要文件排序? -出于相同的原因,product_id, epoch_id
是从PK中保留的
实际上category_id
确实很重要,我们可以从ORDER BY
中消除它。
那么,这是否意味着MySQL将遍历聚集索引并检索按默认值排序的所有行,然后不需要重新排序?
答案 0 :(得分:2)
您的问题
我在EXPLAINs
中感到困惑。有人说“ ALL”;有人说“ index..PRIMARY”。好吧,对于InnoDB,这些实际上是相同的。 PRIMARY KEY
与数据一起聚集在同一B + Tree中。
({EXPLAIN
是在InnoDB之前的日子里写的,主要是针对MyISAM,它没有对PK进行聚类。)
EXPLAIN
不够详细,无法清晰回答您的问题。 EXPLAIN FORMAT=JSON
更好,但可能仍然不够清晰。
至于为什么缺少“文件排序”,请考虑一下。如果category
是常数,那么您实际上希望按(product_id, epoch_id)
进行排序。这就是表的子集排序的顺序。如果您尝试了任何其他组合(例如,首先使用epoch_id
),则需要对其进行排序。您的第二种情况和第三种情况(忽略常量category_id
之后)都是如此。
对于第三季度:是的,它等效于ORDER BY product_id, category_id, epoch_id。
“那么,这是否意味着MySQL将遍历聚集索引并检索按默认值排序的所有行,然后就不需要对其重新排序了?” -是的“文件排序”是这种情况的准确(但不完整)指示。
在GROUP BY x ORDER BY b
的情况下,需要2种排序,但是EXPLAIN
仅显示一次。 (EXPLAIN FORMAT=JSON
确实提供了详细信息。)
让我讨论一下此查询:
SELECT *
FROM prices
where category_id = 1
ORDER BY product_id, category_id, epoch_id;
优化器有两种处理方法。
where category_id = 1
),希望没有太多符合该限制的行。ORDER BY
,希望避免排序的节省比遍历整个表进行过滤的节省更多。在给出两种类似情况时,它会收集一些统计信息(可能不尽人意),以合理地推测出执行查询的方式。也许主要的统计数据(在您的示例中)是表格中有category=1
的百分比。
比方说,只有少数几行有category=1
。那么这是最优的:
PRIMARY KEY(category_id, product_id, epoch_id)
在这种情况下,PK的“聚集”性质将通过仅查找和读取行category=1
(加上另外一行来知道它已停止)来执行查询。 B + Tree既可用于查找此类第一行,又可用于扫描所有此类行。
或者,假设PK无法更改。然后可以使用辅助INDEX(category_id)
。它将在B + Tree中扫描该索引,然后(一个接一个地)跳转到数据中以查找行。
无论使用哪种索引,这种情况都将以对找到的行进行排序结束。
比方说,只有大量行具有category=1
。那么这是最优的:
PRIMARY KEY(product_id, category_id, epoch_id)
那样,它可以避免排序(也称为“文件排序”)。但是,它将读取所有行,并跳过所有不包含category=1
的行。
如果您无法更改PK,那么辅助INDEX(product_id, category_id, epoch_id)
会有所帮助。但是,在二级索引和数据BTree之间来回跳跃会非常昂贵。
哪个更好?优化器会选择哪个?很难说。