Mysql CPU 100% - 没有慢查询

时间:2015-09-12 11:41:04

标签: php mysql ajax

我有一个Web应用程序通过vpn调用mysql数据库。

有时(当很多客户使用系统时)来自远程mysql主机的cpu达到100%并且一切都变得很慢(我甚至得到错误500)。

我打开了慢速查询日志,没有任何内容。

我看到了进程列表,并且有一些查询(大约30个,这是正常的,因为我使用了很多ajax),他们正在对数据进行排序。

执行该操作的查询(填写了数字):

SELECT cs.id_client as id_client,  TIMESTAMPDIFF(SECOND,cs.call_start,cs.call_end) AS realduration, cs.*, cs.call_start + INTERVAL 0 HOUR as call_start_corrected, cs.call_end + INTERVAL 0 HOUR as call_end_corrected,`c`.`cost` AS `cscost` ,`c`.`call_rate` AS `ccall_rate`
FROM `callscallshop` AS `cs` LEFT JOIN
     `calls` AS `c`
     ON `c`.`call_start` = `cs`.`call_start`
WHERE `c`.`caller_id` = `cs`.`caller_id` AND 
      `cs`.`id_client` IN (9301) AND
      year(cs.call_start) = year(now()) AND 
      month(cs.call_start) = month(now()) AND 
      week(cs.call_start) = week(now()) AND
      `cs`.`invoice_id` = '-1'
ORDER BY cs.call_start DESC

我在数据库的本地副本上运行以上操作,并且在执行时获得0.0027。

我删除时差和排序和差异时间执行是如此之小..

在谷歌浏览器上,我发现ajax响应有时超过30秒。

数据库服务器的任务管理器显示主要负载是mysql.exe

你们是否有任何线索可以帮助我解决这个问题?

您认为是查询还是服务器的某些内容?

更新

Ajax呼叫每天在等待中等待30秒! 它似乎是cpu使用率达到100%。

他们不允许在服务器上应用索引。

我会感激任何想法。

2 个答案:

答案 0 :(得分:2)

由于此条款的存在,您的LEFT JOIN被强制为INNER JOIN

 WHERE c.caller_id = cs.caller_id

改为编写你的ON子句,你将解决这个问题。

ON c.call_start = cs.call_start AND c.caller_id = cs.caller_id

您向我们展示的查询可以得到显着优化,尤其是在您的calllscallshop表有很多行的情况下。为什么?您正在以 unsargeable 方式进行日期范围搜索。我们来解决这个问题。

你似乎处于生产紧急状态 - 500消息和所有 - 所以让我们先把事情整理好,最简单。

首先,在calllscallshop表格上创建compound covering index。让我们来看看。您的查询在caller_idinvoice_id上查找相等性。它在id_client上查找集合包含。它需要在call_start上进行范围扫描。

因此,我们将此索引添加到calllscallshop(caller_id, invoice_id, call_start, id_client)。尽快做到这一点,看看你的问题是否有所改善。您可以在不更改软件行的情况下添加索引。

另外,请将此索引添加到calls(caller_id, call_start)

其次,我们需要更有效地进行本周搜索。作为奖励,我们所做的更改将解决您在每个日历年开始时遇到的问题。你是这样做的。

  year(cs.call_start) = year(now()) AND    /* slow */
  month(cs.call_start) = month(now()) AND  /* slow */
  week(cs.call_start) = week(now())        /* slow, wrong at year-end */

如果您希望此搜索具有可搜索性,则需要使用此搜索:

    cs.call_start >= midnight on the first day of this week
AND cs.call_start <  midnight on the first day of next week

这种编写查询的方式使MySQL能够对call_start上的索引进行范围扫描搜索。它可以惊人地快。

所以问题是,我们如何提出midnight on the first day of this week

如果您的周数从周日开始,这个小公式可以解决问题。

 CURDATE()-INTERVAL DAYOFWEEK(CURDATE())-1 DAY

这是有效的,因为DAYOFWEEK()返回星期日= 1,星期一= 2等。因此,为了获得任何一天的前一个星期日,我们会提前DAYOFWEEK()-1天。

所以让我们写下你的日期范围搜索:

    cs.call_start >= CURDATE()-INTERVAL DAYOFWEEK(CURDATE())-1 DAY
AND cs.call_start <  CURDATE()-INTERVAL DAYOFWEEK(CURDATE())-1 DAY + INTERVAL 7 DAY

这一改变应该有助于您的查询。

作为奖励,你可以这样做

GROUP BY DATE(call_start)-INTERVAL DAYOFWEEK(DATE(call_start))-1 DAY 

如果需要,可以获取每周的数据摘要。

还有一件事:当您处理生产数据时,执行SELECT *被认为是有害的。相反,您应该指定所需的确切列。

答案 1 :(得分:0)

Ollies建议非常有用,但没有解决问题。

问题不是直接在sql中,而是与php代码有关。

更清楚:

  • 每个callshop都有多个致电舱。
  • 我正在为每个小屋创建一个连接并执行查询。

解决问题的原因是为多个小屋执行查询,因此每秒的连接和查询执行次数较少。

ajax呼叫响应从30秒下降到850ms!

感谢Ollie,您的建议使我的代码更快更清洁。