从 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