查询时间太长且未使用索引

Query taking too long and not using index

我们有一个事件服务,其中 returns 事件按 ID 和事件时间戳过滤并按主列排序。

此 table

中大约有 1.5 GB 的数据

查询:

SELECT event.eventID, event.orgID, event.objectType, event.action, event.objectID, 
       event.logEventID, event.eventTimestamp, event.userID, event.source, 
       event.additionalDetails, event.insertByUserID, event.insertDateTime, 
       event.modifyByUserID, event.modifyDateTime 
  FROM event 
 WHERE event.orgID = 100 
   AND event.eventTimestamp >= 1535046151000 
ORDER BY event.eventID ASC limit 10001;

以上查询执行需要 14 秒。

如果我删除 ORDER BY event.eventID ASC,需要 0.01 秒

当前索引在主列 idx1(eventID) 上。我们添加了第二个索引 idx2(orgID,eventTimestamp),但仍然没有看到性能提升。

除非用 "USE Hint" 指定,否则查询不使用新索引。使用提示和提供 idx2 需要 7 秒。

我们正在使用 mysql 版本 5.6

对缩短执行时间有什么想法吗?

最糟糕的是 Using filesort 操作,我们应该看看是否可以在 "index order" 中获取行 returned 以避免该操作。

我很想添加一个索引:

... ON `event` (`orgid`,`eventid`,`eventtimestamp`)

我还会尝试调整查询。虽然不是绝对必要的,但我们可以在 ORDER BY 子句中包含 orgid 列,因为 WHERE 子句中的条件保证我们只有一个值。

 ORDER BY event.orgid ASC, event.eventid ASC

这里的目的是为优化器提供尽可能多的信息,即有一个合适的索引来满足 ORDER BY 子句。

使用EXPLAIN查看执行计划。

我们正在尝试让 MySQL 在 "index" 中的 orgid 到 return 行上使用索引范围扫描,按 eventid 排序。然后丢弃不满足 eventtimestamp.

条件的行
SELECT event.eventid
     , event.orgid
     , event.objecttype
     , event.action
     , event.objectid
     , event.logeventid
     , event.eventtimestamp
     , event.userid
     , event.source
     , event.additionaldetails
     , event.insertbyuserid
     , event.insertdatetime
     , event.modifybyuserid
     , event.modifydatetime
  FROM event
 WHERE event.orgid = 100
   AND event.eventtimestamp >= 1535046151000
 ORDER
    BY event.orgid ASC 
     , event.eventid ASC
 LIMIT 10001

如果这不足以避免 "Using filesort" 操作,那么我们可以尝试将 eventtimestamp 上的条件从 WHERE 子句中移到 HAVING 子句中。 (将 AND 关键字替换为 HAVING。)


省略 eventtimestamp 的索引可能足以获得合理的执行计划。

而不是

... ON `event` (`orgid`,`eventid`,`eventtimestamp`)

这可能同样有效

... ON `event` (`orgid`,`eventid`)

没有必要同时使用 WHEREHAVING。只需使用 WHERE orgID = 100 AND eventTimestamp >= somevalue.

SELECT lots of stuff ORDER BY something LIMIT count 是臭名昭著的性能反模式。为什么?它对一大堆行进行排序只是为了丢弃其中的大部分。

您可以使用 延迟连接 来改善这一点。在子查询中获取所需行的 PK 值,然后检索详细信息。

为子查询尝试这样的事情。

         SELECT eventID
           FROM event
          WHERE orgID = 100
            AND eventTimestamp >= somevalue
          ORDER BY eventID
          LIMIT somecount

您可以使用 (orgID, eventTimestamp) 上的复合索引加速此查询。 (如果 table 使用 MyISAM,像这样 (orgID, eventTimestamp, eventID).

在索引中包含 PK

然后执行此操作以从您想要的行中获取数据详细信息。

SELECT event.eventID, event.orgID, event.objectType, event.action, event.objectID, 
       event.logEventID, event.eventTimestamp, event.userID, event.source, 
       event.additionalDetails, event.insertByUserID, event.insertDateTime, 
       event.modifyByUserID, event.modifyDateTime 
  FROM event
  JOIN (
         SELECT eventID
           FROM event
          WHERE orgID = 100
            AND eventTimestamp >= somevalue
          ORDER BY eventID
          LIMIT somecount
       ) sel ON event.eventID  = sel.eventID
 ORDER BY event.eventID

之所以有效,是因为它只排序然后丢弃主键值。那更便宜。

如果您的 eventTimestamp 和 eventID 值都严格按升序排列,则还有另一种优化可能。也就是说,如果您插入的每一行都有当前时间戳,您就可以利用这一事实。

SELECT event.eventID, event.orgID, event.objectType, event.action, event.objectID, 
       event.logEventID, event.eventTimestamp, event.userID, event.source, 
       event.additionalDetails, event.insertByUserID, event.insertDateTime, 
       event.modifyByUserID, event.modifyDateTime 
  FROM event
  JOIN (
         SELECT eventID
           FROM event
          WHERE orgID = 100
            AND eventID >= (SELECT MIN(eventID) 
                              FROM event 
                             WHERE eventTimestamp >= somevalue)
          ORDER BY eventID
          LIMIT somecount
       ) sel ON event.eventID  = sel.eventID
 ORDER BY event.eventID

orgID 上使用索引,在 eventTimestamp 上使用另一个索引来进行此类查询。它之所以有效,是因为每一行的时间戳 >= 比你的起始时间戳都有一个 eventID >= 第一行中与时间标准匹配的那个。

希望您的 eventTimestamp 列具有 BIGINTDOUBLE 数据类型。 Javascript 时间戳(自 UNIX 纪元以来的毫秒数)不适合 32 位整数。如果你正确地索引事物,更大的数据类型不会对性能产生太大影响。

有这两个:

INDEX(orgid, eventTimestamp)
INDEX(orgid, eventID)

优化器可以使用其中任何一个,并且可以根据统计数据选择更好的一个。向其中任何一个添加额外的列都不会加速 this 查询。第二个索引将避免文件排序,但可能不会更快。

如果输出到网页,我建议 LIMIT 10001 很笨重。