如何优化此mysql查询,其中包括带有纪元时间范围的where子句?

问题描述

我正在尝试优化以下mySQL查询

SELECT events.id,events.tracking_id,events.event_time,events.event_type_id
FROM events
WHERE events.event_time >= 1564617600000000 AND events.event_time <= 1567295999000000

以下是事件表的详细信息:

CREATE TABLE `events` (
  `id` char(36) NOT NULL,`tracking_id` char(72) NOT NULL,`event_time` bigint(16) NOT NULL,`server_id` char(36) NOT NULL,`project_id` char(36) NOT NULL,`data_type_id` char(36) NOT NULL,`event_type_id` char(36) NOT NULL,PRIMARY KEY (`tracking_id`,`event_time`),KEY `id_idx` (`id`),KEY `server_id_idx` (`server_id`),KEY `event_type_id_idx` (`event_type_id`),KEY `event_time_idx` (`event_time`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

解释输出

+----+-------------+--------+------------+------+----------------+------+---------+------+---------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys  | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+--------+------------+------+----------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | events | NULL       | ALL  | event_time_idx | NULL | NULL    | NULL | 2877592 |    37.48 | Using where |
+----+-------------+--------+------------+------+----------------+------+---------+------+---------+----------+-------------+

查询大约需要30秒才能运行。并且在event_time上添加索引似乎对执行时间没有任何影响-看起来好像没有在使用索引?

event_time最初是一个字符(36),但是随后出现以下警告:'由于对字段'event_time'进行类型或排序规则转换,因此无法使用索引'event_time_idx'进行范围访问,因为我将event_time转换为bigint,但仍未使用索引。

我该怎么做才能提高此查询性能(实际上是更大查询中的子查询)?

解决方法

表中的所有行,或至少其中的大部分都符合条件吗?换句话说,您提供的时间戳记是从2019-08-01 00:00:00到2019-08-31 23:59:59,所以整整一个月。这个月以来,表中当前是否有大多数行?

MySQL进行基于成本的优化。它估计读取索引条目,然后使用该索引查找行的成本。这意味着每个索引条目进行两次查找,再加上一些开销。

MySQL在某些情况下估计表扫描可能比使用索引更好,这是正确的。该阈值没有记录,但是根据我的经验,如果它估计匹配的行数超过表的20%,则倾向于进行表扫描。 YMMV

您可以使用index hint告诉MySQL,它应该将表扫描视为无限昂贵,因此,如果可以完全使用索引,则应该使用它。

SELECT events.id,events.tracking_id,events.event_time,events.event_type_id
FROM events FORCE INDEX (event_time_idx)
WHERE events.event_time >= 1564617600000000 AND events.event_time <= 1567295999000000

但是请记住,MySQL基于成本的优化器可能是正确的。实际上,根据数据的不同,进行表扫描的成本可能更低。