MYSQL GROUP BY和WHERE索引,带有时间戳列

时间:2018-10-19 16:35:09

标签: mysql indexing group-by

我已经实现了此查询:

#include <stdio.h>
#define __type_is_void(expr) _Generic((typeof(expr)*){0}, void*:1, default:0)
#define __expr_or_zero(expr) _Generic((typeof(expr)*){0}, void*:0, default:(expr))

#define DO(expr) \
    _Generic((typeof(expr)*){0}, \
        void*:__DO_VOID(expr), \
        default:__DO(__expr_or_zero(expr)))

#define __DO(expr) \
    ({ typeof(expr) __ret; puts("do nonvoid"); __ret = (expr); __ret; })

#define __DO_VOID(expr) \
    (void)({ puts("do void"); (void)(expr); })

void foo(void) { }
int bar(void) { return 1; }

int main(void) 
{
    DO(foo());
    DO(bar());
    return 0;
}

事件表具有数百万条记录。列时间戳是DATETIME,其他列是INT。该表经常被访问并且具有很多索引。

开始时,此查询花费了超过10分钟的时间来执行。我通过添加新索引来解决此问题

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;

这很好,我在不到2秒的时间内得到了结果。

我遇到的问题是当我更改条件“间隔30天”时。例如,如果我设置INTERVAL 50 DAY,则MYSQL不使用我创建的索引。相反,它使用仅覆盖两列的另一个索引。

说明命令:

ALTER TABLE Event 
    ADD INDEX `Event_timestamp_subtype_userId_storeId` (`timestamp` ASC, `subType` ASC, `userId` ASC, `storeId` ASC);

说明输出:

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;

因此,如果放置50天条件,查询是不可行的。如何使该查询使用正确的索引而不依赖于参数的值?

我正在使用mysql服务器5.7.23

谢谢!

致谢

1 个答案:

答案 0 :(得分:1)

您有“很多索引”。弹枪无济于事。您是否有这些索引,并按给定的顺序排列了

INDEX(subtype, timestamp)
INDEX(subtype, userID)

优化器可能希望对WHERE使用其中任何一个。而且,由于它无法消耗所有WHERE(由于2个范围),因此它不会到达GROUP BY中的列。

第一列(subtype)已通过=测试;这很容易。
第二列是“范围”,因此这是它可以处理的最后一件事。

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

INDEX(subtype, timestamp, storeID, userID)
INDEX(subtype, userID, timestamp, storeID)

现在处理只需要查看索引的BTree,而不必在该BTree和具有数据的BTree之间反弹。

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

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