我有一个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%。
他们不允许在服务器上应用索引。
我会感激任何想法。
答案 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_id
和invoice_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代码有关。
更清楚:
解决问题的原因是为多个小屋执行查询,因此每秒的连接和查询执行次数较少。
ajax呼叫响应从30秒下降到850ms!
感谢Ollie,您的建议使我的代码更快更清洁。