【发布时间】:2019-03-24 14:15:20
【问题描述】:
我已经实现了这个查询:
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;
Event 表有数百万条记录。列时间戳为 DATETIME,其他列为 INT。这张表被频繁访问并且有很多索引。
一开始,这个查询的执行时间超过了 10 分钟。我通过添加一个新索引来解决这个问题
ALTER TABLE Event
ADD INDEX `Event_timestamp_subtype_userId_storeId` (`timestamp` ASC, `subType` ASC, `userId` ASC, `storeId` ASC);
这很好,我在不到 2 秒的时间内就有了结果。
我遇到的问题是当我更改条件 INTERVAL 30 DAY 时。如果我设置 INTERVAL 50 DAY(例如),MYSQL 不会使用我创建的索引。相反,它使用另一个仅涵盖两列的索引。
解释命令:
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
谢谢!
问候
【问题讨论】:
-
您在dba.stackexchange.com 上提出这个问题可能会更幸运但是,您是否尝试过在定义索引时更改列的顺序? MySQL 可能决定您的索引没有足够的选择性,因为该范围内有大量时间戳。如果你把例如user_id 或 store_id 首先它可能会更好。
-
就个人而言,我会尝试一个避免“使用文件排序”操作的执行计划,并在
(subtype,userid,storeid,timestamp)上使用覆盖索引(我可能会将查询修改为GROUP BY subtype,userid,storeid)