我有一张很大的桌子,每天大约有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值,唯一的区别是“日期之间”子句!
表架构
`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()哈希进行分区,所有结果都具有相同的令人失望的结果。
有人吗?
答案 0 :(得分:1)
通常用{partition key“列开始PRIMARY KEY
效率很低。毕竟,首先是“分区修剪”。为什么然后过滤相同的内容?
通常,使用将在“范围”测试中使用的列来启动任何复合索引都是低效率的。这很微妙,但是我认为这可以解释您所看到的差异。使用IN
(第二个查询),可以使用PK中的更多字段,从而运行得更快。
而且,不,Optimizer非常聪明,足以理解“日期”的工作方式。否则,它可能会像第一个查询一样快地执行第二个查询。 (这粗略地处理了一些评论。)
(供参考)
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
中的 个
unit_id
。 (我怀疑这是大多数效率所在。)enum_key
的行record_date
是否正确。我很高兴设置start
和future
分区。 (也许您已经读过this。)
注意:拥有大约50个分区可能有点效率低下。如果您拥有(或将要拥有)更多资源,请考虑使用每周或每月分区。这将对 my PK产生轻微影响,但要等到第4步。
对于PARTITION BY HASH
...我发现使用它没有没有的性能提升。 (或者至少没有其他方法无法实现的。)