从 MySQL 中的分区 table 到 SELECT 的最佳性能方式是什么:显式分区选择,使用 WHERE 子句修剪,或两者兼而有之?
What's the best way performance-wise to SELECT from a PARTITIONed table in MySQL: explicit partition selection, pruning with a WHERE clause, or both?
假设你有:
CREATE TABLE demo (
amount ,
year ,
cycle ,
otherStuff ,
PRIMARY KEY ( id , year , cycle )
) ENGINE = INNODB
PARTITION BY RANGE ( year )
SUBPARTITION BY KEY ( cycle )
SUBPARTITIONS 12 (
PARTITION p2020 VALUES LESS THAN (2021) ,
PARTITION p2021 VALUES LESS THAN (2022) ,
PARTITION p2022 VALUES LESS THAN (2023) ,
PARTITION pmax VALUES LESS THAN MAXVALUE
);
在 table 上 SELECT 到 运行 哪个最好?
甲:
SELECT otherStuff FROM demo WHERE amount > 10 AND year = 2022 AND cycle = 1;
乙:
SELECT otherStuff FROM demo (p2022, p1) WHERE amount > 10;
或
C:
SELECT otherStuff FROM demo (p2022, p1) WHERE amount > 10 AND year = 2022 AND cycle = 1;
我确信 p运行ing 中有一些额外的开销 — 存储引擎采取一些初步步骤来确定哪些分区与 WHERE 子句匹配。但是,只有一个分区和子分区与 WHERE 子句匹配,而 p运行ing WHERE 子句仅包含简单的等于比较,我想弄清楚的是额外的开销是否对性能来说是名义上的。我想弄清楚的原因是因为我想知道我是否可以使用 p运行ing,这在设计上提供了一个优势:如果我想的话,我可以摆脱我的分区并拥有没有要更改的查询。换句话说,显式分区选择引入了我宁愿避免的依赖性。
谢谢。
None 以上。即“A”,但没有任何分区。
摆脱分区,除非你能展示它的一些用途。
仅在某些应用程序中 PARTITON
有助于提高性能。我从未发现 SUBPARTITION
.
的性能用途
WHERE 金额 > 10 AND year = 2022 AND cycle = 1
最好由
处理
INDEX(year, cycle, -- in either order
amount) -- put 'range' after '='
分区对这个查询没有帮助。
时间序列
“时间序列”可以存储在分区 table 中,其中每个分区是一周或一个月(或其他时间范围)。但是,当您准备好删除或存档“旧”行时,唯一的优势就来了。
DROP PARTITION
比等效 DELETE
更快且侵入性更小。但是,它假定最旧的“周”可以完全抛弃。
同时,SELECTs
没有 性能优势。这样想。分区修剪将选择(可能)一个分区进行查找,然后索引接管。但修剪并不是“免费”的。沿着 BTree 走也不是。 BTree 可能 更浅一层,因为分区服务于一层“树”。但这仅仅意味着 SELECT
正在用一种搜索机制换取另一种搜索机制——可能没有任何性能变化。
更多关于时间序列和如何分区的信息:http://mysql.rjweb.org/doc.php/partitionmaint这还包括如何随着时间的推移有效地创建 'next' 分区。
如果你不想DROP
旧分区,但想“归档”它,那么分区有助于“transportable tablespaces”,其中分区已从主 table 中删除并单独变成 table。然后可以 'transported' 到其他地方。同样,这仅适用于完整分区,因此移动的行必须与使用的 PARTITION BY ...
对齐。
分区的其他用途
见上文link;我只发现了另外 4 个案例;它们比时间序列更晦涩。
覆盖索引
索引太复杂,无法做出许多笼统的陈述。如果覆盖索引有两个列都在用范围(例如,BETWEEN
)进行测试,则查询注定是低效的。本质上,BTree 索引只能处理一个范围。这导致了分区很少见的用途——对一个“范围”使用分区修剪,对另一个“范围”使用索引。
在地球上查找“附近”的地方可以使用索引中 PARTITION BY RANGE(latitude)
和 longitude
的二维查找。
我不认为这个技巧在 2 个范围之外可行。
返回“覆盖”...如果使用覆盖索引的 WHERE
子句有多个范围,仍然存在性能问题。
关于“覆盖”索引的另一件事 -- 有时它们因为具有“太多”列而变得笨拙。我使用的经验法则是“不要在 INDEX
中放置超过 5 列”。 (这是一个非常软的规则;“5”没有什么神奇的。)
最佳指数(es)
我们可以一次讨论一个问题,但这还不够。一个 table 通常会被许多不同的 Select 击中。要找到最佳索引,我们需要一次查看所有主要查询。
如果一个 Select 请求 INDEX(a)
而另一个请求 INDEX(a,b)
,同时拥有两个索引会适得其反。还是把短的去掉比较好
我上面的建议是 (year, cycle, amount)
或 (cycle, year, amount)
。可能 另一个 查询会在它们之间进行选择。或者,也许查询中有足够多的变化需要 both 变体。
关于索引的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql
假设你有:
CREATE TABLE demo (
amount ,
year ,
cycle ,
otherStuff ,
PRIMARY KEY ( id , year , cycle )
) ENGINE = INNODB
PARTITION BY RANGE ( year )
SUBPARTITION BY KEY ( cycle )
SUBPARTITIONS 12 (
PARTITION p2020 VALUES LESS THAN (2021) ,
PARTITION p2021 VALUES LESS THAN (2022) ,
PARTITION p2022 VALUES LESS THAN (2023) ,
PARTITION pmax VALUES LESS THAN MAXVALUE
);
在 table 上 SELECT 到 运行 哪个最好?
甲:
SELECT otherStuff FROM demo WHERE amount > 10 AND year = 2022 AND cycle = 1;
乙:
SELECT otherStuff FROM demo (p2022, p1) WHERE amount > 10;
或
C:
SELECT otherStuff FROM demo (p2022, p1) WHERE amount > 10 AND year = 2022 AND cycle = 1;
我确信 p运行ing 中有一些额外的开销 — 存储引擎采取一些初步步骤来确定哪些分区与 WHERE 子句匹配。但是,只有一个分区和子分区与 WHERE 子句匹配,而 p运行ing WHERE 子句仅包含简单的等于比较,我想弄清楚的是额外的开销是否对性能来说是名义上的。我想弄清楚的原因是因为我想知道我是否可以使用 p运行ing,这在设计上提供了一个优势:如果我想的话,我可以摆脱我的分区并拥有没有要更改的查询。换句话说,显式分区选择引入了我宁愿避免的依赖性。
谢谢。
None 以上。即“A”,但没有任何分区。
摆脱分区,除非你能展示它的一些用途。
仅在某些应用程序中 PARTITON
有助于提高性能。我从未发现 SUBPARTITION
.
WHERE 金额 > 10 AND year = 2022 AND cycle = 1
最好由
处理INDEX(year, cycle, -- in either order
amount) -- put 'range' after '='
分区对这个查询没有帮助。
时间序列
“时间序列”可以存储在分区 table 中,其中每个分区是一周或一个月(或其他时间范围)。但是,当您准备好删除或存档“旧”行时,唯一的优势就来了。
DROP PARTITION
比等效 DELETE
更快且侵入性更小。但是,它假定最旧的“周”可以完全抛弃。
同时,SELECTs
没有 性能优势。这样想。分区修剪将选择(可能)一个分区进行查找,然后索引接管。但修剪并不是“免费”的。沿着 BTree 走也不是。 BTree 可能 更浅一层,因为分区服务于一层“树”。但这仅仅意味着 SELECT
正在用一种搜索机制换取另一种搜索机制——可能没有任何性能变化。
更多关于时间序列和如何分区的信息:http://mysql.rjweb.org/doc.php/partitionmaint这还包括如何随着时间的推移有效地创建 'next' 分区。
如果你不想DROP
旧分区,但想“归档”它,那么分区有助于“transportable tablespaces”,其中分区已从主 table 中删除并单独变成 table。然后可以 'transported' 到其他地方。同样,这仅适用于完整分区,因此移动的行必须与使用的 PARTITION BY ...
对齐。
分区的其他用途
见上文link;我只发现了另外 4 个案例;它们比时间序列更晦涩。
覆盖索引
索引太复杂,无法做出许多笼统的陈述。如果覆盖索引有两个列都在用范围(例如,BETWEEN
)进行测试,则查询注定是低效的。本质上,BTree 索引只能处理一个范围。这导致了分区很少见的用途——对一个“范围”使用分区修剪,对另一个“范围”使用索引。
在地球上查找“附近”的地方可以使用索引中 PARTITION BY RANGE(latitude)
和 longitude
的二维查找。
我不认为这个技巧在 2 个范围之外可行。
返回“覆盖”...如果使用覆盖索引的 WHERE
子句有多个范围,仍然存在性能问题。
关于“覆盖”索引的另一件事 -- 有时它们因为具有“太多”列而变得笨拙。我使用的经验法则是“不要在 INDEX
中放置超过 5 列”。 (这是一个非常软的规则;“5”没有什么神奇的。)
最佳指数(es)
我们可以一次讨论一个问题,但这还不够。一个 table 通常会被许多不同的 Select 击中。要找到最佳索引,我们需要一次查看所有主要查询。
如果一个 Select 请求 INDEX(a)
而另一个请求 INDEX(a,b)
,同时拥有两个索引会适得其反。还是把短的去掉比较好
我上面的建议是 (year, cycle, amount)
或 (cycle, year, amount)
。可能 另一个 查询会在它们之间进行选择。或者,也许查询中有足够多的变化需要 both 变体。
关于索引的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql