Mysql-按月分区扫描所有分区,而不是一个分区

Mysql-按月分区扫描所有分区,而不是一个分区,mysql,partitioning,Mysql,Partitioning,我创建了一个表,如下所示: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired datetime NOT NULL DEFAULT '2000-01-01') PARTITION BY RANGE ( Month(hired) ) ( PARTITION p1 VALUES LESS THAN (2), PARTITION p2 VALUES LESS THAN (3), PA

我创建了一个表,如下所示:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired datetime NOT NULL DEFAULT '2000-01-01')
PARTITION BY RANGE ( Month(hired) ) (
PARTITION p1 VALUES LESS THAN (2),
PARTITION p2 VALUES LESS THAN (3),
PARTITION p3 VALUES LESS THAN (4),
PARTITION p4 VALUES LESS THAN (5),
PARTITION p5 VALUES LESS THAN (6),
PARTITION p6 VALUES LESS THAN (7),
PARTITION p7 VALUES LESS THAN (8),
PARTITION p8 VALUES LESS THAN (9),
PARTITION p9 VALUES LESS THAN (10),
PARTITION p10 VALUES LESS THAN (11),
PARTITION p11 VALUES LESS THAN (12),
PARTITION p12 VALUES LESS THAN maxvalue
);
ALTER TABLE employees partition BY range(Year(hired))
PARTITION p1 VALUES LESS THAN (2001),
PARTITION p2 VALUES LESS THAN (2005),
PARTITION p3 VALUES LESS THAN (2010),
PARTITION p4 VALUES LESS THAN (2015),
PARTITION p5 VALUES LESS THAN MAXVALUE);
正如您在上面看到的,按月分区已经完成

接下来,我向表中添加一些记录

插入记录后,我查询表以验证它是否正在从预期的分区获取数据

当我提出以下问题时

EXPLAIN PARTITIONS SELECT COUNT(*) 
FROM employees 
WHERE hired BETWEEN'2015-01-01' AND '2015-03-01';
理想情况下,它必须扫描分区p1、p2和p3。 但是解释结果显示它正在扫描所有分区

我将分区从月份略微更改为年份,如下所示:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired datetime NOT NULL DEFAULT '2000-01-01')
PARTITION BY RANGE ( Month(hired) ) (
PARTITION p1 VALUES LESS THAN (2),
PARTITION p2 VALUES LESS THAN (3),
PARTITION p3 VALUES LESS THAN (4),
PARTITION p4 VALUES LESS THAN (5),
PARTITION p5 VALUES LESS THAN (6),
PARTITION p6 VALUES LESS THAN (7),
PARTITION p7 VALUES LESS THAN (8),
PARTITION p8 VALUES LESS THAN (9),
PARTITION p9 VALUES LESS THAN (10),
PARTITION p10 VALUES LESS THAN (11),
PARTITION p11 VALUES LESS THAN (12),
PARTITION p12 VALUES LESS THAN maxvalue
);
ALTER TABLE employees partition BY range(Year(hired))
PARTITION p1 VALUES LESS THAN (2001),
PARTITION p2 VALUES LESS THAN (2005),
PARTITION p3 VALUES LESS THAN (2010),
PARTITION p4 VALUES LESS THAN (2015),
PARTITION p5 VALUES LESS THAN MAXVALUE);
现在我像以前一样询问:

EXPLAIN PARTITIONS SELECT COUNT(*)
FROM employees
WHERE hired BETWEEN '2015-01-01' and '2015-03-01';
结果表明,它仅从分区p5获取数据

我不知道为什么它适用于按年份划分,而不是按月份划分。同样的问题也出现在日常生活中


请帮助我了解MySQL为何会以这种方式运行

您发现了另一种情况,
分区
没有用处

BETWEEN '2015-01-01' and '2015-03-01'; -- could have been optimized
BETWEEN '2015-01-01' and '2016-03-01'; -- must touch all partitions
分区修剪代码太笨,无法区分两者之间的区别

即使修剪按预期工作,查询也不会比使用
索引(租用)
的非分区表快多少。您是否有任何可能更有效的查询


.

您发现了另一种情况,
分区
没有用处

BETWEEN '2015-01-01' and '2015-03-01'; -- could have been optimized
BETWEEN '2015-01-01' and '2016-03-01'; -- must touch all partitions
分区修剪代码太笨,无法区分两者之间的区别

即使修剪按预期工作,查询也不会比使用
索引(租用)
的非分区表快多少。您是否有任何可能更有效的查询


.

MySQL参考手册中记录了有关“月”分区与“年”分区的MySQL行为:

摘录

。。。只要分区表达式包含一个等式或一个范围,可以将其缩减为一组等式,或者当分区表达式表示递增或递减关系时,就可以应用优化

当分区表达式使用YEAR()或TO_DAYS()函数时,也可以对在DATE或DATETIME列上分区的表应用修剪。此外,在MySQL 5.7中,当分区表达式使用TO_SECONDS()函数时,可以对此类表应用修剪


我认为问题的根源在于月份(datecol)不是一种“升序”关系。如果您指定的范围是“2014-11-01”到“2015-02-01”,那么将是分区p11、p12,然后是环绕到p01、p02

查询中的范围是一个特例

(请注意,按年份()和TO_DAYS()划分确实代表了一种升序关系。也就是说,列中较高的值永远不会换回较低的分区。)

要使用monthwise分区进行分区修剪,我想您可以尝试在分区表达式上添加查询相等谓词。例如:

   WHERE MONTH(hiredate) IN (1,2)
     AND hiredate >= '2015-01-01'
     AND hiredate < '2015-03-01'
这样,即使您没有得到任何分区修剪,检查每个分区也会有一点开销。但是执行范围检查并不需要对分区中的每一行进行完全扫描。MySQL可以在索引上使用范围扫描操作,有效地检查一个分区中的许多分区是否存在“此处未找到行”

归根结底,正是由于缺乏合适的指数,才会扼杀业绩


分区并不是提高查询性能的灵丹妙药。索引也不是银弹,但适当的索引比分区更贵、更闪亮、更子弹形。

MySQL关于“每月”分区与“每年”分区的行为在MySQL参考手册中有记录:

摘录

。。。只要分区表达式包含一个等式或一个范围,可以将其缩减为一组等式,或者当分区表达式表示递增或递减关系时,就可以应用优化

当分区表达式使用YEAR()或TO_DAYS()函数时,也可以对在DATE或DATETIME列上分区的表应用修剪。此外,在MySQL 5.7中,当分区表达式使用TO_SECONDS()函数时,可以对此类表应用修剪


我认为问题的根源在于月份(datecol)不是一种“升序”关系。如果您指定的范围是“2014-11-01”到“2015-02-01”,那么将是分区p11、p12,然后是环绕到p01、p02

查询中的范围是一个特例

(请注意,按年份()和TO_DAYS()划分确实代表了一种升序关系。也就是说,列中较高的值永远不会换回较低的分区。)

要使用monthwise分区进行分区修剪,我想您可以尝试在分区表达式上添加查询相等谓词。例如:

   WHERE MONTH(hiredate) IN (1,2)
     AND hiredate >= '2015-01-01'
     AND hiredate < '2015-03-01'
这样,即使您没有得到任何分区修剪,检查每个分区也会有一点开销。但是执行范围检查并不需要对分区中的每一行进行完全扫描。MySQL可以在索引上使用范围扫描操作,有效地检查一个分区中的许多分区是否存在“此处未找到行”

归根结底,正是由于缺乏合适的指数,才会扼杀业绩


分区并不是提高查询性能的灵丹妙药。索引也不是银弹,但适当的索引比分区更贵、更闪亮、更子弹形。

请确保代码格式正确。这次我已经为你做了。请确保你的代码格式正确。这次我是为你做的。