MYSQL GROUP BY和WHERE索引,带有timestamp列

MYSQL GROUP BY和WHERE索引,带有timestamp列,mysql,indexing,group-by,Mysql,Indexing,Group By,我已经实现了这个查询: SELECT evt.userId, evt.storeId, COUNT(1) AS totalVisits FROM Event evt WHERE evt.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW() AND evt.subtype = 2 AND userID IS NOT NULL GROUP BY userId, stor

我已经实现了这个查询:

SELECT 
    evt.userId, evt.storeId, COUNT(1) AS totalVisits
FROM
    Event evt
WHERE
    evt.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW()
    AND 
    evt.subtype = 2 
    AND 
    userID IS NOT NULL
GROUP BY userId, storeId
HAVING totalVisits>16;
事件表有数百万条记录。列timestamp为DATETIME,其他列为INT。此表经常被访问,并且有很多索引

开始时,执行此查询需要10分钟以上。我通过添加一个新索引来解决这个问题

ALTER TABLE Event 
    ADD INDEX `Event_timestamp_subtype_userId_storeId` (`timestamp` ASC, `subType` ASC, `userId` ASC, `storeId` ASC);
这很好,我在不到2秒钟内就得到了结果

我遇到的问题是,我每隔30天改变一次条件。如果我设置INTERVAL 50 DAY(例如),MYSQL就不会使用我创建的索引。相反,它使用另一个只包含两列的索引

Explain命令:

EXPLAIN EXTENDED SELECT 
    evt.userId, evt.storeId, COUNT(1) AS totalVisits
FROM
    Event evt
WHERE
    evt.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 50 DAY) AND NOW()
    AND 
    evt.subtype = 2 
    AND 
    evt.userID IS NOT NULL
GROUP BY userId, storeId
HAVING totalVisits>16;
解释输出:

+----+-------------+-------+------------+------+------------------------------------------------------------------------------------------------------------+-----------------------------+---------+-------+---------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys                                                                                              | key                         | key_len | ref   | rows    | filtered | Extra                                                               |
+----+-------------+-------+------------+------+------------------------------------------------------------------------------------------------------------+-----------------------------+---------+-------+---------+----------+---------------------------------------------------------------------+
|  1 | SIMPLE      | evt   | NULL       | ref  | Event_userId_index,Event_subType_storeId_index,Event_timetamp_index,Event_timestamp_subtype_userId_storeId | Event_subType_storeId_index | 3       | const | 7375964 |    25.00 | Using index condition; Using where; Using temporary; Using filesort |
+----+-------------+-------+------------+------+------------------------------------------------------------------------------------------------------------+-----------------------------+---------+-------+---------+----------+---------------------------------------------------------------------+
因此,如果放置一个50天的条件,查询是不可侵犯的。如何使此查询独立于参数值而使用正确的索引

我使用的是mysql服务器5.7.23

谢谢

关于

你有“很多索引”。猎枪没用。您是否有这些索引,并且列的顺序是给定的

优化器可能希望对
WHERE
使用这两种方法中的任何一种。而且,由于它不能使用WHERE中的所有
(因为有两个范围),因此它无法通过
到达
组中的列

第一列(
子类型
)用
=
测试;这很容易。
第二列是“范围”,因此这是它最不能处理的事情

通过将这些索引中的每一个都变成一个“覆盖”索引,可以有一个小的改进:

现在,处理只需要查看索引的BTree,而不必在该BTree和包含数据的BTree之间跳转

(前两列按特定顺序排列;其他两列可以互换。)


如果这是一个“巨大”的表(数百万行),我们可以讨论另一个优化,因为您实际上需要一个2D索引。

您可能更幸运地问这个问题,但是,您是否在定义索引时尝试过更改列的顺序?MySQL可能认为索引的选择性不够,因为在这个范围内有大量的时间戳。如果你先输入例如user\u id或store\u id,效果可能会更好。就个人而言,我会尝试执行一个避免“使用文件排序”操作的执行计划,在
(subtype,userid,storeid,timestamp)
(我可能会将查询修改为
按子类型分组,userid,storeid
)我用一个覆盖索引解决了这个问题。。。但是字段的顺序是subtype、storeId、userId和timestamp。我测试了其他组合,但这是最好的。查询在不到1秒的时间内运行,没有文件排序或昂贵的操作。谢谢
INDEX(subtype, timestamp)
INDEX(subtype, userID)
INDEX(subtype, timestamp, storeID, userID)
INDEX(subtype, userID, timestamp, storeID)