我们有一个事件服务,该服务返回按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”指定,否则查询不使用新索引。使用提示并提供idx2需要7秒钟。
我们使用的是mysql版本5.6
是否有缩短执行时间的想法?
答案 0 :(得分:1)
最大的麻烦是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
如果这还不足以避免执行“使用文件排序”操作,那么我们可以尝试将eventtimestamp
上的条件从WHERE
子句移到HAVING
子句中。 (将AND
关键字替换为HAVING
。)
省略eventtimestamp
的索引可能足以获取合理的执行计划。
代替
... ON `event` (`orgid`,`eventid`,`eventtimestamp`)
这可能同样有效
... ON `event` (`orgid`,`eventid`)
答案 1 :(得分:0)
无需同时使用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,则像这样(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> =第一行中符合时间标准的eventID。
希望您的eventTimestamp列具有BIGINT
或DOUBLE
数据类型。 Javascript时间戳(自UNIX时代以来的毫秒数)不适合32位整数。如果索引正确,则较大的数据类型不会对性能造成太大影响。
答案 2 :(得分:0)
同时具备以下两个条件:
INDEX(orgid, eventTimestamp)
INDEX(orgid, eventID)
优化器可以使用它们之一,并且可以根据统计信息选择更好的一个。向其中任一列添加额外的列将不会加快 this 查询。第二个索引将避免文件排序,但可能可能不会更快。
如果输出要转到网页,则我认为LIMIT 10001
太笨拙了。