MySQL: ID查找时如何避免所有分区扫描(基于年份)?
MySQL: How avoid all partitions scan (year-based) when doing ID lookup?
如果我有一个 table 按年划分的;当我必须通过其 ID 查找行并且不能在查找查询中使用分区修剪时,如何避免扫描所有分区?
CREATE TABLE part_table (
id bigint NOT NULL auto_increment,
moment datetime NOT NULL,
KEY (id),
KEY (moment)
)-- partitioning information (in years)
PARTITION BY RANGE( YEAR(moment) ) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION pFuture VALUES LESS THAN (maxvalue) )
;
例如查找查询:
SELECT * FROM part_table WHERE ID = <nr>
- 你不想要
PRIMARY KEY(id, moment)
或 PRIMARY KEY(moment, id)
而不是 INDEX(id)
吗?
- 索引已分区。每个分区本质上是一个“table”。它有一个用于数据和 PK 的 BTree,以及一个用于每个二级索引的 BTree。
- 因此,要找到
id=123
需要检查每个分区中的 INDEX(id)
。这就是 PARTITIONed
table 有时 比等效的未分区 table. 慢 的原因之一
- 预先创建未来的分区(一个除外)是低效的。
告诉我们您的主要问题。我可能会解释为什么不应该对 table 进行分区。我在您的定义中看到两个可能的好处:
- 丢弃 'old' 数据比
DELETEing
快得多。
- `在 ..
之间的其他内容和时刻
部分案例
对于此讨论,我假设以某种方式(BY RANGE(TO_DAYS(moment))
或 BY ... (YEAR(moment))
等)按日期时间进行分区。
WHERE id BETWEEN 111 and 222
分区可能会造成轻微伤害,因为无论有什么索引可用,查询都必须在每个分区中查找。
WHERE id BETWEEN 111 and 222
AND moment > NOW() - INTERVAL 1 MONTH
with some index starting with `id`
这是分区“p运行ing”有益的情况。它将查找一个或两个分区(取决于查询是否在一月份 运行)。然后它会稍微有效地使用索引来查找 id
.
如果索引以 id
开头(并假设上面的 WHERE
子句之一:
PRIMARY KEY(id, moment)
PK 与数据“聚集”在一起。即数据先id
后moment
排序。因此 id BETWEEN...
将在 BTree 中连续查找行——这是最有效的。 AND moment...
用于过滤 out 一些行。
INDEX(id)
不是“集群”。它是一个二级索引。二级索引有两个步骤。 (1) 在二级 BTree 中搜索 ids,但不按 moment
过滤; (2) 使用为您提供的人工 PK 进入数据 BTree; (3) 现在可以通过 moment
进行过滤。更多的步骤,更多的阅读块等
DROP PARTITION p2020
id 比 `DELETE .. WHERE moment < '2021-01-01'.
快很多
更多
查看所有主要查询很重要。 X=constant
与 X BETWEEN...
可以在优化方面产生很大差异;请提供适合您的应用的具体示例。
此外,有时“覆盖”索引可以弥补其他低效索引。因此,这些示例需要显示重要查询中的所有列。它们是什么数据类型。
在没有这样的细节的情况下,我将作以下广泛的陈述(可能因细节而无效):
- 如果
WHERE
仅引用一列,则 PARTITIONing
可能永远没有用。
- 如果
WHERE
有一个 =
测试和一个 'range' 测试,可能有一个比分区更好的复合索引。
- 分区 可能 在有两个范围测试时表现出色,但前提是 'pruning' 可以应用。 (p运行ing有很多限制。)
- 有 2 个范围,未被 p运行ed 的那个应该在
PRIMARY KEY
. 的开头
- 当使用 p运行ing 但
WHERE
的其余部分不能使用某些索引时,这意味着对分区进行扫描。如果只有几个分区,那可能是一个大扫描。
- 不要预先建立一个以上的分区。当不 p运行ing 时,打开所有分区却发现有些分区是空的,这有点昂贵。
如果我有一个 table 按年划分的;当我必须通过其 ID 查找行并且不能在查找查询中使用分区修剪时,如何避免扫描所有分区?
CREATE TABLE part_table (
id bigint NOT NULL auto_increment,
moment datetime NOT NULL,
KEY (id),
KEY (moment)
)-- partitioning information (in years)
PARTITION BY RANGE( YEAR(moment) ) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION pFuture VALUES LESS THAN (maxvalue) )
;
例如查找查询:
SELECT * FROM part_table WHERE ID = <nr>
- 你不想要
PRIMARY KEY(id, moment)
或PRIMARY KEY(moment, id)
而不是INDEX(id)
吗? - 索引已分区。每个分区本质上是一个“table”。它有一个用于数据和 PK 的 BTree,以及一个用于每个二级索引的 BTree。
- 因此,要找到
id=123
需要检查每个分区中的INDEX(id)
。这就是PARTITIONed
table 有时 比等效的未分区 table. 慢 的原因之一
- 预先创建未来的分区(一个除外)是低效的。
告诉我们您的主要问题。我可能会解释为什么不应该对 table 进行分区。我在您的定义中看到两个可能的好处:
- 丢弃 'old' 数据比
DELETEing
快得多。 - `在 .. 之间的其他内容和时刻
部分案例
对于此讨论,我假设以某种方式(BY RANGE(TO_DAYS(moment))
或 BY ... (YEAR(moment))
等)按日期时间进行分区。
WHERE id BETWEEN 111 and 222
分区可能会造成轻微伤害,因为无论有什么索引可用,查询都必须在每个分区中查找。
WHERE id BETWEEN 111 and 222
AND moment > NOW() - INTERVAL 1 MONTH
with some index starting with `id`
这是分区“p运行ing”有益的情况。它将查找一个或两个分区(取决于查询是否在一月份 运行)。然后它会稍微有效地使用索引来查找 id
.
如果索引以 id
开头(并假设上面的 WHERE
子句之一:
PRIMARY KEY(id, moment)
PK 与数据“聚集”在一起。即数据先id
后moment
排序。因此 id BETWEEN...
将在 BTree 中连续查找行——这是最有效的。 AND moment...
用于过滤 out 一些行。
INDEX(id)
不是“集群”。它是一个二级索引。二级索引有两个步骤。 (1) 在二级 BTree 中搜索 ids,但不按 moment
过滤; (2) 使用为您提供的人工 PK 进入数据 BTree; (3) 现在可以通过 moment
进行过滤。更多的步骤,更多的阅读块等
DROP PARTITION p2020
id 比 `DELETE .. WHERE moment < '2021-01-01'.
快很多更多
查看所有主要查询很重要。 X=constant
与 X BETWEEN...
可以在优化方面产生很大差异;请提供适合您的应用的具体示例。
此外,有时“覆盖”索引可以弥补其他低效索引。因此,这些示例需要显示重要查询中的所有列。它们是什么数据类型。
在没有这样的细节的情况下,我将作以下广泛的陈述(可能因细节而无效):
- 如果
WHERE
仅引用一列,则PARTITIONing
可能永远没有用。 - 如果
WHERE
有一个=
测试和一个 'range' 测试,可能有一个比分区更好的复合索引。 - 分区 可能 在有两个范围测试时表现出色,但前提是 'pruning' 可以应用。 (p运行ing有很多限制。)
- 有 2 个范围,未被 p运行ed 的那个应该在
PRIMARY KEY
. 的开头
- 当使用 p运行ing 但
WHERE
的其余部分不能使用某些索引时,这意味着对分区进行扫描。如果只有几个分区,那可能是一个大扫描。 - 不要预先建立一个以上的分区。当不 p运行ing 时,打开所有分区却发现有些分区是空的,这有点昂贵。