【问题标题】:What would be making this query so incredibly slow? (MySQL 5.6.36)是什么让这个查询变得如此慢? (MySQL 5.6.36)
【发布时间】:2017-06-23 09:29:46
【问题描述】:

我有一个不断增长的数据库表,大约有 800 万行正在运行,我的应用程序会定期从中获取数据。但是,查询突然开始锁定整个系统。有大量 mysqld 进程阻塞了所有 CPU 内核。

可能是数据库的大小不断增加吗?或者下面的查询中是否有某些内容可能导致它运行这么长时间?例如UNIX_TIMESTAMP?这是慢查询日志的摘录。查询每分钟执行一次,查询时间始终在 ~7 左右。

# Query_time: 6.839524  Lock_time: 0.000170 Rows_sent: 277  Rows_examined: 7989334
FROM (
    SELECT @row := @row + 1 AS `row`, `timestamp`, `price`
    FROM (
        SELECT @row := 0
    ) `derived_1`, `items`
    WHERE `price` IS NOT NULL
        AND `timestamp` >= UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY)
        AND `currency` = 'EUR'
        AND `type` = 'icon'
    ORDER BY `timestamp` ASC
) `derived_2`
WHERE `row` % 8 = 0;

尝试一下有点困难,因为它是一个生产环境。我也无法在我的开发环境中重现该问题。

如果您需要任何其他信息,请告诉我!

提前致谢!

【问题讨论】:

  • 您对 where 子句中的字段是否有索引或表的索引是什么?
  • 请发布EXPLAIN的输出。
  • 你能分享一下执行计划吗?
  • 我们执行查询时生成的,表示数据传输。
  • @Thierry 这不好,如果您的 where 子句中的字段没有索引,则数据库引擎必须扫描表中的所有条目,显然也是为了排序。因此,随着行数的增加,它会变得越来越慢。

标签: mysql sql cpu mysql-slow-query-log


【解决方案1】:

创建索引,以便 DBMS 可以快速找到记录。此索引应包含WHERE 子句的列,从与= 进行比较的列开始。

CREATE idx ON items (type, currency, price, timestamp);

这甚至恰好是一个覆盖索引,即它包含您在查询中使用的所有列。因此 DBMS 甚至不必读取表,因为它可以从索引本身获取所有数据。

【讨论】:

  • 这是我最喜欢的答案!一开始我没想过要覆盖索引!
  • @nCessity:谢谢。不过,您关于选择性的评论也是有效的。因此,可能值得尝试两个覆盖索引,一个以时间戳开头,一个不以时间戳开头,然后查看 DBMS 选择的索引。
【解决方案2】:

最好的索引是这样的:

INDEX(type, currency,  -- The two columns tested via '=' (in either order)
      timestamp,       -- then this, for a range test
      price)           -- finally, the rest of the columns used

这将是一个“覆盖”索引,因此它不必在索引 BTree 和数据 BTree 之间反弹。 (因此,其他建议的索引会更慢。)

希望这将使查询运行速度超过 7 秒。

“每分钟”运行一些东西是不安全的。如果出于某种原因,一个实例未能在一分钟内完成,会发生什么?可能多次调用会互相绊倒,并且可能会变得越来越慢。这可能一直在堵塞你的经历。

为避免这种情况,请让作业持续运行——计算查询,然后休眠,例如 53 秒。这将(基于当前时间)非常接近每分钟一次。或者它可以暂停足够的时间以到达下一分钟(但不少于 0 秒)。

【讨论】:

    【解决方案3】:

    时间戳索引应该会给您带来更好的性能。特别是,因为时间戳列的值可能分布良好。货币和类型的索引可能会带来另一个性能提升。但也许你甚至没有注意到,因为,只有几种不同的货币和数据类型(与 800 万行相比)

    CREATE INDEX idx_timestamp ON items (timestamp);
    

    是的,这需要一些时间!

    UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY) 不是问题,因为它只计算一次。

    【讨论】:

    • 非常感谢!我一定会试试的。出于好奇:为什么只有timestamp 对索引有用,而currencytype 也没有?这与它们的不变性有关吗?
    • 另外,索引这种大小的表是否会导致插入/更新/...行出现问题?我应该考虑这样的停机时间吗?
    • @Thierry:别担心这个。是的,插入、更新和删除需要更长的时间,但我想我们在这里谈论的是微秒。批量更新通常不是必需的,批量插入通常不会发生,所以只有批量删除可能会发生并受到一点影响,但这是要付出的代价:-)
    • 非常感谢@nCessity!
    猜你喜欢
    • 2014-11-04
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 2012-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多