查询耗时太长,未使用索引

nhn9ugyo  于 2021-06-20  发布在  Mysql
关注(0)|答案(3)|浏览(397)

我们有一个事件服务,它返回按id和事件时间戳过滤并按主列排序的事件。
此表中约有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”指定,否则查询不会使用新索引。使用use提示并提供idx2需要7秒钟。
我们使用的是MySQL5.6版本
有没有关于提高执行时间的想法?

hkmswyz6

hkmswyz61#

同时具备以下两个条件:

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

优化器可以使用它们中的任何一个,并且可以根据统计信息选择更好的一个。向任一列添加额外列都不会加快此查询的速度。第二个索引将避免文件排序,但可能不会更快。
如果输出是一个网页,我建议 LIMIT 10001 很难操作。

b4qexyjb

b4qexyjb2#

没有必要两者都用 WHERE 以及 HAVING . 只是使用 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) . (如果表使用myisam,那么在索引中包含pk,如下所示 (orgID, 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 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>=第一行中与时间标准匹配的eventid。
希望您的eventtimestamp列 BIGINT 或者 DOUBLE 数据类型。javascript时间戳(unix纪元后的毫秒数)不适合32位整数。如果索引正确,较大的数据类型不会对性能造成太大的差异。

6ie5vjzr

6ie5vjzr3#

最糟糕的是 Using filesort 操作时,我们应该看看是否可以获得按“索引顺序”返回的行,以避免该操作。
我很想添加一个索引:

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

我还尝试调整查询。虽然不是严格必要的,但我们可以包括 orgid 中的列 ORDER BY 条款,因为 WHERE 条款向我们保证单一价值。

ORDER BY event.orgid ASC, event.eventid ASC

这里的目的是给优化器尽可能多的信息,有一个合适的索引来满足 ORDER BY 条款。
使用 EXPLAIN 看看执行计划。
我们正在尝试让mysql使用索引范围扫描 orgid 按“索引”顺序返回行的步骤 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`)

相关问题