MySql 分区 table - select 在日期范围之间使用 PK 与日期 IN (...) 子句相比非常慢
MySql partitioned table - select between date range using PK very slow compared to date IN (...) clause
我有一个非常大的 table,每天包含大约 300 万条记录。
下面的查询太慢了
EXPLAIN SELECT *
FROM summary_by_to_days_range
WHERE(record_date BETWEEN '2019-03-12' AND '2019-03-15')
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
GROUP BY unit_id, record_date
ORDER BY record_date DESC;
结果如下:
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
| rows | filtered | Extra | possible_keys | key | partitions |
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
| 9072566 | 4 | Using where | PRIMARY | PRIMARY | from20190312,from20190313,from20190314,from20190315 |
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
与
相比
EXPLAIN SELECT *
FROM summary_by_to_days_range
WHERE(record_date IN ('2019-03-12','2019-03-13','2019-03-14','2019-03-15'))
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
GROUP BY unit_id, record_date
ORDER BY record_date DESC;
更好的结果:
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
| rows | filtered | Extra | possible_keys | key | partitions |
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
| 16 | 100 | Using where | PRIMARY | PRIMARY | from20190312,from20190313,from20190314,from20190315 |
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
我不明白为什么..我提供的是 PK 值,唯一的区别是 Between date 子句!
Table 架构
`CREATE TABLE summary_by_to_days_range (
`record_date` date NOT NULL,
`unit_id` int(11) NOT NULL,
`enum_key` int(11) NOT NULL,
`str_value` varchar(200) DEFAULT NULL,
PRIMARY KEY (`record_date`,`unit_id`,`enum_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE (TO_DAYS(record_date))
(PARTITION START_h VALUES LESS THAN (0) ENGINE = InnoDB,
PARTITION from20181231 VALUES LESS THAN (737425) ENGINE = InnoDB,
PARTITION from20190101 VALUES LESS THAN (737426) ENGINE = InnoDB,
.
.
PARTITION future VALUES LESS THAN MAXVALUE ENGINE = InnoDB)`
我也尝试过按键分区,按范围列按 DAYOFYEAR() 的散列进行分区,所有这些都产生了同样令人失望的结果..
有人吗?
通常 以 "partition key" 的列开始 PRIMARY KEY
效率低下。毕竟,先到"partition pruning";为什么然后过滤相同的东西?
通常 使用将在 "range" 测试中使用的列来启动任何复合索引是低效的。这很微妙,但我认为这可以解释您所看到的差异。使用 IN
(您的第二个查询),可以使用 PK 中的更多字段,从而 运行 更快。
而且,不,优化器足够聪明,可以理解 "dates" 的工作原理。否则,它执行第二个查询的速度可能与第一个一样快。 (这粗略地解决了一些评论。)
(供参考)
WHERE record_date BETWEEN '2019-03-12' AND '2019-03-15'
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
`record_date` date NOT NULL,
PRIMARY KEY (`record_date`,`unit_id`,`enum_key`)
PARTITION BY RANGE (TO_DAYS(record_date))
让我们按以下方式处理我的两条评论。
改为
PRIMARY KEY(unit_id, enum_key, record_date)
有了这个 PK, SELECTs
将
- 修剪成 4 个分区(和以前一样)
- 快速筛选出所需
unit_id
。 (我怀疑这是效率最高的地方。)
- 跳过
enum_key
的行
- 检查
record_date
是否正确。
很高兴设置 start
和 future
分区。 (也许你已经读过this。)
注意:超过 50 个分区可能会有些低效。如果您拥有(或将拥有)更多,请考虑使用每周或每月分区。这对myPK会有轻微影响,但要到第4步才会有影响。
至于 PARTITION BY HASH
... 我发现使用它 没有 性能提升。 (或者至少 none 无法通过其他方式实现。)
我有一个非常大的 table,每天包含大约 300 万条记录。
下面的查询太慢了
EXPLAIN SELECT *
FROM summary_by_to_days_range
WHERE(record_date BETWEEN '2019-03-12' AND '2019-03-15')
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
GROUP BY unit_id, record_date
ORDER BY record_date DESC;
结果如下:
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
| rows | filtered | Extra | possible_keys | key | partitions |
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
| 9072566 | 4 | Using where | PRIMARY | PRIMARY | from20190312,from20190313,from20190314,from20190315 |
+---------+----------+-------------+---------------+---------+-----------------------------------------------------+
与
相比EXPLAIN SELECT *
FROM summary_by_to_days_range
WHERE(record_date IN ('2019-03-12','2019-03-13','2019-03-14','2019-03-15'))
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
GROUP BY unit_id, record_date
ORDER BY record_date DESC;
更好的结果:
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
| rows | filtered | Extra | possible_keys | key | partitions |
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
| 16 | 100 | Using where | PRIMARY | PRIMARY | from20190312,from20190313,from20190314,from20190315 |
+------+----------+-------------+---------------+---------+-----------------------------------------------------+
我不明白为什么..我提供的是 PK 值,唯一的区别是 Between date 子句!
Table 架构
`CREATE TABLE summary_by_to_days_range (
`record_date` date NOT NULL,
`unit_id` int(11) NOT NULL,
`enum_key` int(11) NOT NULL,
`str_value` varchar(200) DEFAULT NULL,
PRIMARY KEY (`record_date`,`unit_id`,`enum_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE (TO_DAYS(record_date))
(PARTITION START_h VALUES LESS THAN (0) ENGINE = InnoDB,
PARTITION from20181231 VALUES LESS THAN (737425) ENGINE = InnoDB,
PARTITION from20190101 VALUES LESS THAN (737426) ENGINE = InnoDB,
.
.
PARTITION future VALUES LESS THAN MAXVALUE ENGINE = InnoDB)`
我也尝试过按键分区,按范围列按 DAYOFYEAR() 的散列进行分区,所有这些都产生了同样令人失望的结果..
有人吗?
通常 以 "partition key" 的列开始 PRIMARY KEY
效率低下。毕竟,先到"partition pruning";为什么然后过滤相同的东西?
通常 使用将在 "range" 测试中使用的列来启动任何复合索引是低效的。这很微妙,但我认为这可以解释您所看到的差异。使用 IN
(您的第二个查询),可以使用 PK 中的更多字段,从而 运行 更快。
而且,不,优化器足够聪明,可以理解 "dates" 的工作原理。否则,它执行第二个查询的速度可能与第一个一样快。 (这粗略地解决了一些评论。)
(供参考)
WHERE record_date BETWEEN '2019-03-12' AND '2019-03-15'
AND unit_id = 1148210
AND enum_key IN (9, 10, 38, 311)
`record_date` date NOT NULL,
PRIMARY KEY (`record_date`,`unit_id`,`enum_key`)
PARTITION BY RANGE (TO_DAYS(record_date))
让我们按以下方式处理我的两条评论。
改为
PRIMARY KEY(unit_id, enum_key, record_date)
有了这个 PK, SELECTs
将
- 修剪成 4 个分区(和以前一样)
- 快速筛选出所需
unit_id
。 (我怀疑这是效率最高的地方。) - 跳过
enum_key
的行
- 检查
record_date
是否正确。
很高兴设置 start
和 future
分区。 (也许你已经读过this。)
注意:超过 50 个分区可能会有些低效。如果您拥有(或将拥有)更多,请考虑使用每周或每月分区。这对myPK会有轻微影响,但要到第4步才会有影响。
至于 PARTITION BY HASH
... 我发现使用它 没有 性能提升。 (或者至少 none 无法通过其他方式实现。)