【问题标题】:Optimize mysql settings/table for performance under load针对负载下的性能优化 mysql 设置/表
【发布时间】:2014-08-31 20:09:44
【问题描述】:

我对队列系统有以下查询。困扰我的最慢的查询是这个:

UPDATE workers SET work = $workid, last_used = NOW() 
WHERE status = 1 AND work IS NULL ORDER BY last_used ASC LIMIT 1

当没有负载时,查询会在大约0.04 秒内执行,但是当许多 php 脚本正在执行此查询时,执行时间会越来越长,直到 40.0 秒,这是一个大问题。

该表有大约40.000 条目,并且有一个statusliking_media 的索引。查询的 EXPLAIN 显示解析器正在使用与 statusliking_media 的交集,并获得大约 3000 行以使用 ORDER_BY 处理。 EXPLAIN 显示更多 using where; using filesort

它背后的 VPS 有 8 个核心 @ 2.5ghz,12GB RAM。当查询运行非常慢时,只有低 CPU 使用率。当负载开始上升时,CPU 使用率会高得多。

当运行许多 php 脚本时,如何在负载下大大提高此查询的性能?我可以调整一般的 mysql 设置来修复它吗?还是表架构不好或缺少索引?我希望能够在不损失性能的情况下每秒运行大约 300 个此类查询。

【问题讨论】:

  • 听起来好像有别的东西在锁表。可能其他查询需要优化
  • 在一个while循环中依次执行5个查询以获得作业。我已经记录了所有这些的执行时间,它总是只显示我在这里发布的查询的长执行时间,其他的保持很快。只有这 5 个查询会经常执行,因此速度变慢的查询必须在这 5 个查询范围内。由于我发布的查询总是很慢,我怀疑这是问题所在,有意义吗?
  • workers 表是否有唯一的 id 字段?这将有助于使用子查询摆脱 ORDER BY。
  • 它有一个 worker_id 作为主要但 last_used 的事情是我总是希望首先获得最少使用的工人。该查询更新了一个worker条目并将其设置为last_used = NOW(),因此当多次发出查询时不会返回同一个worker,除非它再次成为最少使用的一个。

标签: php mysql sql performance innodb


【解决方案1】:

查询的问题是找到要更新的适当行。对于这个查询:

UPDATE workers
    SET work = $workid, last_used = NOW() 
    WHERE status = 1 AND work IS NULL
    ORDER BY last_used ASC
    LIMIT 1;

您需要以下复合索引:workers(status, work, last_used)

这应该会加快查询速度并防止多个更新相互锁定。

【讨论】:

  • 即使这个查询显示返回 10 000 行而不是我的旧查询的 3 000 行,它似乎执行得更好,但它仍然使用文件排序,当许多查询处于活动状态时,执行时间会增加,一切都会变慢.
  • 即使使用复合索引,它仍然使用“filesort”?这让我很吃惊。
  • 是的,文件排序还在里面。完整的解释显示: id => 1;选择类型 => 简单;表 => 工人;类型 => 范围;可能的键 => 状态;关键 => 状态; key_len => 27;参考 => 常量,常量;行 => 10568;额外 => 使用 where;使用文件排序。您还需要更多信息吗?
  • 它似乎只在使用更新查询时使用文件排序。如果我将该更新换成 SELECT,则使用文件排序就消失了。为什么会这样?
  • @Matthias 。 . .关于order by 优化的文档只讨论了select 而不是update (dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html)。
【解决方案2】:

我认为这应该避免 ORDER BY,它会强制查询获取所有结果,而不仅仅是具有最新 last_used 的结果:

UPDATE workers
    INNER JOIN (
        SELECT MIN(last_used), worker_id 
            FROM workers
    ) AS newest_worker 
    ON newest_workder.worker_id = workers.worker_id
SET workers.work = $workid, workers.last_used = NOW() 
WHERE workers.status = 1 AND workers.work IS NULL

【讨论】:

  • 缺少 LIMIT 1,否则所有工作人员都会立即更新。所以你认为问题出在 ORDER BY 上?
  • MAX 只返回 1 行,因此不需要 LIMIT。
  • 问题是 order by + limit 首先返回所有匹配的行然后减少到 1,而 MAX 只得到一个匹配的行。
  • 为了得到最少使用的,它实际上不是 MIN() 吗?还有一件事:查询执行得很好,但现在我遇到了这个查询的死锁:尝试获取锁时发现死锁;尝试重新启动事务。有什么办法可以解决吗?
  • 对于最早的使用 MIN(),是的。已更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-23
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多