我按如下方式创建了一个表:
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
);
正如您在上面所看到的那样,月份分区已经完成。
接下来,我在表格中添加了一些记录。
插入记录后,我查询表以验证它是否从预期的分区中获取数据。
当我提供以下查询时,
EXPLAIN PARTITIONS SELECT COUNT(*)
FROM employees
WHERE hired BETWEEN'2015-01-01' AND '2015-03-01';
理想情况下,它必须扫描分区,p1,p2和p3。 但解释结果显示它正在扫描所有分区。
我将分区从月份改为年份,如下所示:
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会以这种方式运行。
答案 0 :(得分:1)
您发现了另一种PARTITIONing
没用的案例。
BETWEEN '2015-01-01' and '2015-03-01'; -- could have been optimized
BETWEEN '2015-01-01' and '2016-03-01'; -- must touch all partitions
分区修剪代码太笨了,无法区分两者之间的差异。
即使修剪工作正常,查询也不会比使用INDEX(hired)
的非分区表快。您有任何可能更好的查询吗?
答案 1 :(得分:1)
关于"月刊"的MySQL行为分区与"逐年" MySQL参考手册中记录了分区:
http://dev.mysql.com/doc/refman/5.7/en/partitioning-pruning.html
摘录
只要分区表达式由等式或可以减少到一组等式的范围组成,或者当分区表达式表示增加或减少关系时,就可以应用...优化。
当分区表达式使用 YEAR()或TO_DAYS()函数时,也可以对在 DATE 或DATETIME列上分区的表应用修剪。此外,在MySQL 5.7中,当分区表达式使用TO_SECONDS()函数时,可以对这些表应用修剪。
我认为问题的根源是MONTH(datecol)不是"升序"关系。如果您指定的范围是' 2014-11-01'到' 2015-02-01',这将是分区p11,p12,然后环绕到p01,p02。
查询中的范围是一种特殊情况。
(请注意,按YEAR()和TO_DAYS()执行分区表示升序关系。也就是说,列中较高的值永远不会回绕到较低的分区。)
要使用逐月分区进行分区修剪,我认为您尝试在分区表达式上添加查询等式谓词。例如:
WHERE MONTH(hiredate) IN (1,2)
AND hiredate >= '2015-01-01'
AND hiredate < '2015-03-01'
MySQL 可能能够使用第一个条件进行分区修剪。
或者,性能可能会变得灾难性,MySQL会在表中的每个翻转行上评估表达式(MONTH()函数)。
无论分区方案如何,您都需要定义索引
... ON employees(hiredate)
有了这个,即使你没有得到任何分区修剪,检查每个分区也会有一点开销。但是执行范围检查不需要对分区中的每一行进行全扫描。 MySQL可以对索引使用范围扫描操作,以有效地检查分区中的&#34;没有在这里找到的行&#34;在许多分区中。
最重要的是,缺少适当的指数可以扼杀业绩。
分区并不是查询性能的灵丹妙药。指数也不是银子弹,但适当的指数比分割更贵重金属,光泽和子弹形状。