查询时间太长且未使用索引
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`)
没有必要同时使用 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)
上的复合索引加速此查询。 (如果 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 列具有 BIGINT
或 DOUBLE
数据类型。 Javascript 时间戳(自 UNIX 纪元以来的毫秒数)不适合 32 位整数。如果你正确地索引事物,更大的数据类型不会对性能产生太大影响。
有这两个:
INDEX(orgid, eventTimestamp)
INDEX(orgid, eventID)
优化器可以使用其中任何一个,并且可以根据统计数据选择更好的一个。向其中任何一个添加额外的列都不会加速 this 查询。第二个索引将避免文件排序,但可能不会更快。
如果输出到网页,我建议 LIMIT 10001
很笨重。
我们有一个事件服务,其中 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`)
没有必要同时使用 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)
上的复合索引加速此查询。 (如果 table 使用 MyISAM,像这样 (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 >= 第一行中与时间标准匹配的那个。
希望您的 eventTimestamp 列具有 BIGINT
或 DOUBLE
数据类型。 Javascript 时间戳(自 UNIX 纪元以来的毫秒数)不适合 32 位整数。如果你正确地索引事物,更大的数据类型不会对性能产生太大影响。
有这两个:
INDEX(orgid, eventTimestamp)
INDEX(orgid, eventID)
优化器可以使用其中任何一个,并且可以根据统计数据选择更好的一个。向其中任何一个添加额外的列都不会加速 this 查询。第二个索引将避免文件排序,但可能不会更快。
如果输出到网页,我建议 LIMIT 10001
很笨重。