无法让分区影响查询时间

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

source

请参阅 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) 的虚拟列。)