MySql分区表 - 使用PK的日期范围之间选择与日期IN(...)子句相比非常慢

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySql分区表 - 使用PK的日期范围之间选择与日期IN(...)子句相比非常慢相关的知识,希望对你有一定的参考价值。

我有一个非常大的表,每天包含大约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子句!

表模式

  `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()的哈希值进行分区,所有结果都是相同的令人失望的结果。

任何人?

答案

通常用“分区键”的列启动PRIMARY KEY是低效的。毕竟,首先是“分区修剪”;为什么然后过滤相同的东西?

通常,使用将在“范围”测试中使用的列启动任何复合索引是低效的。这很微妙,但我认为这解释了你所看到的差异。使用IN(您的第二个查询),可以使用PK中的更多字段,从而更快地运行。

而且,不,优化器足够聪明,可以理解“日期”的工作原理。否则,它可以像第一个一样快地进行第二次查询。 (这粗略地解决了一些评论。)

(以供参考)

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都会

  1. 修剪到4个分区(如前所述)
  2. 快速过滤到所需的unit_id。 (我怀疑这是大多数效率的地方。)
  3. 跳过enum_key的行
  4. 检查record_date是否正确。

我很高兴设置startfuture分区。 (也许你已经读过this了。)

注意:拥有超过50个分区可能效率低一些。如果您有(或将有)更多,请考虑使用每周或每月分区。这会对我的PK产生轻微影响,但直到第4步。

至于PARTITION BY HASH ......我发现使用它没有任何性能提升。 (或者至少没有其他方法无法实现。)

以上是关于MySql分区表 - 使用PK的日期范围之间选择与日期IN(...)子句相比非常慢的主要内容,如果未能解决你的问题,请参考以下文章

SQL 按日期范围分区

为日期列添加mysql范围分区

3个日期范围之间的Mysql查询

在日期/时间范围之间选择数据

MySQL:选择两个日期范围内的所有数据

mysql选择范围内的日期时间对象