我有一个表,其中包含一个 month
和一个 year
列。
我有一个查询,通常看起来像 WHERE month=1 AND year=2022
考虑到这张表有多大,我想使用分区和子分区来提高效率。
表 1
查询我需要的数据大约需要 2 分 30 秒。
CREATE TABLE `table_1` (
`id` int NOT NULL AUTO_INCREMENT,
`entity_id` varchar(36) NOT NULL,
`entity_type` varchar(36) NOT NULL,
`score` decimal(4,3) NOT NULL,
`month` int NOT NULL DEFAULT '0',
`year` int NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_month_year` (`month`,`year`, `entity_type`)
)
按“月”划分
查询我需要的数据大约需要 21 秒(很大的改进)。
CREATE TABLE `table_1` (
`id` int NOT NULL AUTO_INCREMENT,
`entity_id` varchar(36) NOT NULL,
`entity_type` varchar(36) NOT NULL,
`score` decimal(4,3) NOT NULL,
`month` int NOT NULL DEFAULT '0',
`year` int NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`,`month`),
KEY `idx_month_year` (`month`,`year`, `entity_type`)
) ENGINE=InnoDB AUTO_INCREMENT=21000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
/*!50100 PARTITION BY LIST (`month`)
(PARTITION p0 VALUES IN (0) ENGINE = InnoDB,
PARTITION p1 VALUES IN (1) ENGINE = InnoDB,
PARTITION p2 VALUES IN (2) ENGINE = InnoDB,
PARTITION p3 VALUES IN (3) ENGINE = InnoDB,
PARTITION p4 VALUES IN (4) ENGINE = InnoDB,
PARTITION p5 VALUES IN (5) ENGINE = InnoDB,
PARTITION p6 VALUES IN (6) ENGINE = InnoDB,
PARTITION p7 VALUES IN (7) ENGINE = InnoDB,
PARTITION p8 VALUES IN (8) ENGINE = InnoDB,
PARTITION p9 VALUES IN (9) ENGINE = InnoDB,
PARTITION p10 VALUES IN (10) ENGINE = InnoDB,
PARTITION p11 VALUES IN (11) ENGINE = InnoDB,
PARTITION p12 VALUES IN (12) ENGINE = InnoDB) */
我想看看我是否可以通过按年分区然后按月子分区来进一步提高性能。我该怎么做?
我不确定以下问题 按年分区和按月划分 mysql 是否相关,没有明显的答案,并且该问题看起来特定于 mysql 5* 和 php。我问的是 mysql 8,从那以后关于分区/子分区/列表列/范围列等没有变化吗?这可以帮助我。
我正在制作更广泛的查询
SELECT
table_1.entity_id AS entity_id,
table_1.entity_type,
table_1.score
FROM table_1
WHERE table_1.month = 12 AND table_1.year = 2022
AND table_1.score > 0
AND table_1.entity_type IN ('type1', 'type2', 'type3', 'type4') # only ever 4 types usually all 4 are present in the query
直接回答你的问题,下面是完成子分区的示例语法。请注意 PRIMARY KEY 必须包括用于分区或子分区的所有列。阅读有关子分区的手册以获取更多信息:https://dev.mysql.com/doc/refman/8.0/en/partitioning-subpartitions.html
模式(MySQL v8.0)
CREATE TABLE `table_1` (
`id` int NOT NULL AUTO_INCREMENT,
`entity_id` varchar(36) NOT NULL,
`entity_type` varchar(36) NOT NULL,
`score` decimal(4,3) NOT NULL,
`month` int NOT NULL DEFAULT '0',
`year` int NOT NULL DEFAULT '0',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`,`month`, `year`),
KEY `idx_month_year` (`month`,`year`, `score`, `entity_type`)
) ENGINE=InnoDB AUTO_INCREMENT=21000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
PARTITION BY LIST (`month`)
SUBPARTITION BY HASH(`year`)
SUBPARTITIONS 10 (
PARTITION p0 VALUES IN (0) ENGINE = InnoDB,
PARTITION p1 VALUES IN (1) ENGINE = InnoDB,
PARTITION p2 VALUES IN (2) ENGINE = InnoDB,
PARTITION p3 VALUES IN (3) ENGINE = InnoDB,
PARTITION p4 VALUES IN (4) ENGINE = InnoDB,
PARTITION p5 VALUES IN (5) ENGINE = InnoDB,
PARTITION p6 VALUES IN (6) ENGINE = InnoDB,
PARTITION p7 VALUES IN (7) ENGINE = InnoDB,
PARTITION p8 VALUES IN (8) ENGINE = InnoDB,
PARTITION p9 VALUES IN (9) ENGINE = InnoDB,
PARTITION p10 VALUES IN (10) ENGINE = InnoDB,
PARTITION p11 VALUES IN (11) ENGINE = InnoDB,
PARTITION p12 VALUES IN (12) ENGINE = InnoDB
);
在您的查询中使用 EXPLAIN 显示该查询仅引用一个子分区。
查询#1
EXPLAIN
SELECT
table_1.entity_id AS entity_id,
table_1.entity_type,
table_1.score
FROM table_1
WHERE table_1.month = 12
AND table_1.year = 2022
AND table_1.score > 0
AND table_1.entity_type IN ('type1', 'type2', 'type3', 'type4');
id | 选择类型 | 桌子 | 分区 | 类型 | 可能的键 | 钥匙 | key_len | 参考 | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单 | table_1 | p12_p12sp2 | 范围 | idx_month_year | idx_month_year | 11 | 1 | 100 | 使用索引条件 |
EXPLAIN中的partitions
字段表明它只访问分区p12_p12sp2
。查询引用的年份,2022,模子分区数 10,将从子分区 2 中读取。
除了按月和年分区外,使用索引也很有帮助。在这种情况下,我将 score
添加到索引中,以便它可以过滤掉 score <= 0
所在的行。 EXPLAIN "Using index condition" 中的注释表明它正在将对 entity_type 的进一步过滤委托给存储引擎。虽然在您的示例中,您说实体类型只有四个值,并且所有四个都被选中,因此该条件无论如何都不会过滤掉任何行。
将日期分成几列通常适得其反。在 SELECT
.
PARTITIONing
usually 对于任何 SELECT
.
分区(或取消分区)时,索引通常需要更改。
对于that查询,我推荐一个组合的date
列,
WHERE date >= '2022-01-01'
AND date < '2022-01-01' + INTERVAL 1 MONTH
和一些 INDEX
从 date
开始。
(您可能还有其他查询;让我们看看其中的一些;他们可能需要不同的索引。)
Covering index -- 这是一个包含all anywhere 在 SELECT
中找到的列的索引。 may 比只有 WHERE
或 WHERE
+ GROUP BY
+ ORDER BY
所需的列更好(更快)。这取决于很多变量。
索引(或 PK)中列的顺序:最左边的列具有优先权。这是磁盘上索引行的顺序。 PK(id, date) 如果按 id
查找(在 WHERE
中)很有用,但如果你只是按日期搜索则没有用。
Sargable -- sargable -- 在函数中隐藏列会禁用索引。那就是 MONTH(date)
不能用 INDEX(date)
.
Blogs -- Index Cookbook and Partition