无法让分区影响查询时间
Trouble getting partitions to make a difference in query time
我只是用一些虚拟数据对分区进行了一些试验,到目前为止我还没有运气优化我的查询。
我从网上下载了一个数据集,由measurements
中的单个table组成:
CREATE TABLE `partitioned_measures` (
`measure_timestamp` datetime NOT NULL,
`station_name` varchar(255) DEFAULT NULL,
`wind_mtsperhour` int(11) NOT NULL,
`windgust_mtsperhour` int(11) NOT NULL,
`windangle` int(3) NOT NULL,
`rain_mm` decimal(5,2) DEFAULT NULL,
`temperature_dht11` int(5) DEFAULT NULL,
`humidity_dht11` int(5) DEFAULT NULL,
`barometric_pressure` decimal(10,2) NOT NULL,
`barometric_temperature` decimal(10,0) NOT NULL,
`lux` decimal(7,2) DEFAULT NULL,
`is_plugged` tinyint(1) DEFAULT NULL,
`battery_level` int(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE (TO_DAYS(measure_timestamp))
(PARTITION `slow` VALUES LESS THAN (736634) ENGINE = InnoDB,
PARTITION `fast` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
就像学习练习一样 我想尝试按 measure_timestamp
划分测量值(没有索引的帮助)。具体来说,我认为尝试将最近一个月单独放在一个分区中会很有趣。 (我知道分区最好大小一样,但我只是想试验一下)
我使用以下命令添加分区(请注意,数据集于 2016 年 12 月结束,并且绝大多数数据点都在之前的月份):
ALTER TABLE partitioned_measures
PARTITION BY RANGE(TO_DAYS(measure_timestamp)) (
PARTITION slow VALUES LESS THAN(TO_DAYS('2016-12-01')),
PARTITION fast VALUES LESS THAN (MAXVALUE)
);
要查询,我正在查看第 2 个及以后的所有条目(只是为了确保我只查看最新的分区):
select SQL_NO_CACHE COUNT(*) FROM partitioned_measures
WHERE measure_timestamp >= '2016-12-02'
AND DAYOFWEEK(measure_timestamp) = 1;
当我在前面添加 EXPLAIN 时,我得到以下内容:
+----+-------------+----------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | partitioned_measures | slow,fast | ALL | NULL | NULL | NULL | NULL | 1835458 | 33.33 | Using where |
+----+-------------+----------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
但是查询时间和分区前差不多(~1.6秒)。我以前从未使用过分区,所以我觉得我缺少一些概念性的东西。
棘手,但我找到了一个可行的解决方案,或者我应该说一个解决方法,它似乎是一个 MySQL 错误?
ALTER TABLE partitioned_measures
PARTITION BY RANGE COLUMNS(measure_timestamp) (
PARTITION slow VALUES LESS THAN('2016-12-01'),
PARTITION fast VALUES LESS THAN(MAXVALUE)
);
参见 demo 正确使用分区修剪
我注意到语法 here
我仍然觉得 partioning puning 不能正常工作很奇怪,
ALTER TABLE partitioned_measures
PARTITION BY RANGE(TO_DAYS(measure_timestamp)) (
PARTITION slow VALUES LESS THAN(TO_DAYS('2016-12-01')),
PARTITION fast VALUES LESS THAN (MAXVALUE)
);
MySQL 5.7 应该可以做 Partition Pruning TO_DAYS()
就好了
Pruning can also be applied for tables partitioned on a DATE or
DATETIME column when the partitioning expression uses the YEAR() or
TO_DAYS() function. In addition, in MySQL 5.7
请参阅 demo which does not use Partition Pruning correct, i've tryed alot 以使其正常工作所有我能想到的方法都失败了。
解释:
它确实 进行了您要求的修剪,但它添加了第一个 分区。为什么?因为那里放了坏日期。
解决方法是使用伪造的第一个分区:
/*!50100 PARTITION BY RANGE (TO_DAYS(measure_timestamp))
({ARTITION bogus VALUES LESS THAN (0) ENGINE = InnoDB, -- any small value
PARTITION `slow` VALUES LESS THAN (736634) ENGINE = InnoDB,
PARTITION `fast` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
参考埋在https://dev.mysql.com/doc/refman/5.7/en/partitioning-handling-nulls.html
如果您有多个分区,您可能会更明显地看到它选择了所需的分区,而且总是第一个。
对于罕见的 exceptions,分区提供的性能并不比使用 suitable 索引的非分区 table 提供的性能更好。在这种情况下,INDEX(measure_timestamp)
。 (或带有 INDEX(dow, measure_timestamp)
的虚拟列。)
我只是用一些虚拟数据对分区进行了一些试验,到目前为止我还没有运气优化我的查询。
我从网上下载了一个数据集,由measurements
中的单个table组成:
CREATE TABLE `partitioned_measures` (
`measure_timestamp` datetime NOT NULL,
`station_name` varchar(255) DEFAULT NULL,
`wind_mtsperhour` int(11) NOT NULL,
`windgust_mtsperhour` int(11) NOT NULL,
`windangle` int(3) NOT NULL,
`rain_mm` decimal(5,2) DEFAULT NULL,
`temperature_dht11` int(5) DEFAULT NULL,
`humidity_dht11` int(5) DEFAULT NULL,
`barometric_pressure` decimal(10,2) NOT NULL,
`barometric_temperature` decimal(10,0) NOT NULL,
`lux` decimal(7,2) DEFAULT NULL,
`is_plugged` tinyint(1) DEFAULT NULL,
`battery_level` int(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE (TO_DAYS(measure_timestamp))
(PARTITION `slow` VALUES LESS THAN (736634) ENGINE = InnoDB,
PARTITION `fast` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
就像学习练习一样 我想尝试按 measure_timestamp
划分测量值(没有索引的帮助)。具体来说,我认为尝试将最近一个月单独放在一个分区中会很有趣。 (我知道分区最好大小一样,但我只是想试验一下)
我使用以下命令添加分区(请注意,数据集于 2016 年 12 月结束,并且绝大多数数据点都在之前的月份):
ALTER TABLE partitioned_measures
PARTITION BY RANGE(TO_DAYS(measure_timestamp)) (
PARTITION slow VALUES LESS THAN(TO_DAYS('2016-12-01')),
PARTITION fast VALUES LESS THAN (MAXVALUE)
);
要查询,我正在查看第 2 个及以后的所有条目(只是为了确保我只查看最新的分区):
select SQL_NO_CACHE COUNT(*) FROM partitioned_measures
WHERE measure_timestamp >= '2016-12-02'
AND DAYOFWEEK(measure_timestamp) = 1;
当我在前面添加 EXPLAIN 时,我得到以下内容:
+----+-------------+----------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | partitioned_measures | slow,fast | ALL | NULL | NULL | NULL | NULL | 1835458 | 33.33 | Using where |
+----+-------------+----------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
但是查询时间和分区前差不多(~1.6秒)。我以前从未使用过分区,所以我觉得我缺少一些概念性的东西。
棘手,但我找到了一个可行的解决方案,或者我应该说一个解决方法,它似乎是一个 MySQL 错误?
ALTER TABLE partitioned_measures
PARTITION BY RANGE COLUMNS(measure_timestamp) (
PARTITION slow VALUES LESS THAN('2016-12-01'),
PARTITION fast VALUES LESS THAN(MAXVALUE)
);
参见 demo 正确使用分区修剪
我注意到语法 here
我仍然觉得 partioning puning 不能正常工作很奇怪,
ALTER TABLE partitioned_measures
PARTITION BY RANGE(TO_DAYS(measure_timestamp)) (
PARTITION slow VALUES LESS THAN(TO_DAYS('2016-12-01')),
PARTITION fast VALUES LESS THAN (MAXVALUE)
);
MySQL 5.7 应该可以做 Partition Pruning TO_DAYS()
就好了
Pruning can also be applied for tables partitioned on a DATE or DATETIME column when the partitioning expression uses the YEAR() or TO_DAYS() function. In addition, in MySQL 5.7
请参阅 demo which does not use Partition Pruning correct, i've tryed alot 以使其正常工作所有我能想到的方法都失败了。
解释:
它确实 进行了您要求的修剪,但它添加了第一个 分区。为什么?因为那里放了坏日期。
解决方法是使用伪造的第一个分区:
/*!50100 PARTITION BY RANGE (TO_DAYS(measure_timestamp))
({ARTITION bogus VALUES LESS THAN (0) ENGINE = InnoDB, -- any small value
PARTITION `slow` VALUES LESS THAN (736634) ENGINE = InnoDB,
PARTITION `fast` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
参考埋在https://dev.mysql.com/doc/refman/5.7/en/partitioning-handling-nulls.html
如果您有多个分区,您可能会更明显地看到它选择了所需的分区,而且总是第一个。
对于罕见的 exceptions,分区提供的性能并不比使用 suitable 索引的非分区 table 提供的性能更好。在这种情况下,INDEX(measure_timestamp)
。 (或带有 INDEX(dow, measure_timestamp)
的虚拟列。)