【问题标题】:MySQL: Optimizing query for records within date rangeMySQL:优化日期范围内记录的查询
【发布时间】:2011-03-01 22:41:08
【问题描述】:

我有一个表 (logs),其中包含以下列(还有其他的,但这些是重要的):

  • id (PK, int)
  • 时间戳(日期时间)(索引)
  • 持续时间(整数)

基本上,这是一次开始,一次结束的事件的记录。该表目前有几十万行。我预计它会增长到数百万。为了加快查询速度,我添加了另一列和预计算值:

  • EndTime(日期时间)(索引)

为了计算 EndTime,我将 Duration 中的秒数添加到 Timestamp 字段中。

现在我要做的是运行一个查询,其中结果计算开始 (Timestamp) 和结束时间 (EndTime) 超出某个时间点的行数。然后,我想在较长的时间跨度(例如一年)内每秒运行一次此查询。我还想计算从特定时间点开始到特定时间点结束的行数。

我创建了以下查询:

SELECT 
    `dates`.`date`, 
    COUNT(*) AS `total`, 
    SUM(IF(`dates`.`date`=`logs`.`Timestamp`, 1, 0)) AS `new`,
    SUM(IF(`dates`.`date`=`logs`.`EndTime`, 1, 0)) AS `dropped` 
FROM 
    `logs`,
    (SELECT 
        DATE_ADD("2010-04-13 09:45:00", INTERVAL `number` SECOND) AS `date` 
        FROM numbers LIMIT 120) AS dates
WHERE dates.`date` BETWEEN `logs`.`Timestamp` AND `logs`.`EndTime` 
GROUP BY `dates`.`date`;

请注意,数字表仅用于轻松枚举日期范围。它是一个包含一列 number 的表,包含值 1、2、3、4、5 等...

这正是我要找的……一张有 4 列的表格:

  • 日期
  • total(在当前时间点之外开始和结束的总行数)
  • 新的(此时开始的行)
  • 已删除(在此时间点结束的行)

问题是,这个查询可能需要很长时间才能执行。要经过 120 秒(如查询所示),大约需要 10 秒。我怀疑这和我得到它的速度一样快,但我想我会在这里问是否有人对提高此查询的性能有任何想法。

任何建议都会很有帮助。感谢您的宝贵时间。

编辑:我在 Timestamp 和 EndTime 上有索引。

EXPLAIN 在我的查询中的输出:

"id";"select_type";"table";"type";"possible_keys";"key";"key_len";"ref";"rows";"Extra"
"1";"PRIMARY";"<derived2>";"ALL";NULL;NULL;NULL;NULL;"120";"Using temporary; Using filesort"
"1";"PRIMARY";"logs";"ALL";"Timestamp,EndTime";NULL;NULL;NULL;"296159";"Range checked for each record (index map: 0x6)"
"2";"DERIVED";"numbers";"index";NULL;"PRIMARY";"4";NULL;"35546940";"Using index"

当我对我的日志表运行分析时,它显示状态正常。

【问题讨论】:

  • 你索引了什么? EXPLAINing 您的查询的输出是什么?你有analyzedlogs 表吗?
  • @outis,感谢您的提问。我已将信息添加到帖子底部。
  • 分析更多是因为它对查询优化器如何使用索引的影响。现在我很好奇扩展查询计划对您的查询来说是什么,特别是看看查询是否被重写以及如何重写。你会在EXPLAIN EXTENDED 查询之后发布SHOW WARNINGS 的结果吗?

标签: mysql


【解决方案1】:

请注意EXPLAIN 输出中logs 表的连接类型为“ALL”且键为NULL,这意味着计划进行全表扫描。 “检查每条记录的范围”消息意味着 MySQL 在检查结果中其他位置的列值后,在 logs 上使用 range access method。我认为这意味着一旦创建了dates,MySQL 就可以使用第二个和第三个索引(可能是TimestampEndTime 上的索引)对logs 执行范围连接,而不是执行全表扫描。如果您只有在 TimestampEndTime 上分别有索引,请尝试在两者上添加索引,这可能会产生更有效的连接类型(例如 index_merge 而不是 range):

CREATE INDEX `start_end` ON `logs` (`Timestamp`, `EndTime`);

我相信(尽管很容易出错)查询计划中的其他项目要么不是真正的问题,要么无法消除。文件排序,作为后者的一个例子,可能是由于GROUP BY。换句话说,这可能是您可以使用此特定查询执行的范围,尽管处理表存储格式的完全不同的查询或方法仍然可能更有效。

【讨论】:

  • 谢谢@outis,这是最有帮助的。我对优化查询不太熟悉,这给了我很多工作要做。我将对您的建议进行更多研究。同时,我想到了一种更有效的方法来做到这一点,将logs 限制为相关条目,并一次完成整个过程 1 天。我将在稍后的每一天在应用程序上分别运行这些查询,并将数据插入到表中以供以后使用。我很快就会发布我最终的结果,但我现在无法做到。我认为我的 ISP 因加载他们的服务器而关闭了我,呵呵。
  • @Brad:如果您还没有,请确保您阅读了query execution plans 上的 MySQL 文档。我的回答是基于该部分的信息。
【解决方案2】:

您可以查看merge tables 以加快处理速度。使用合并表,由于表被拆分,索引更小,从而更快地获取。此外,如果您有多个处理器,搜索可以并行进行,从而提高性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-28
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 2014-09-29
    • 2017-05-26
    • 1970-01-01
    • 2011-08-05
    相关资源
    最近更新 更多