使用DELETE查询MySQL非常慢,查询运行时Apache很奇怪

时间:2011-01-14 21:48:06

标签: mysql apache apache2

首先,从一些细节来描述整体情况:

  • MySQL(5.1.50)数据库,非常强大(32个CPU核心,64GB RAM)FreeBSD 8.1-RELEASE机器,它也运行Apache 2.2。
  • Apache每秒平均获得约50次点击。绝大多数这些点击是针对销售平台的API调用。
  • API调用通常需要大约半秒或更短时间才能生成结果,但最多可能需要30秒才能生成结果。
  • 每个API调用都在数据库中存储一行。存储在那里的信息很重要,但只有大约十五分钟,之后必须到期。
  • 在存储API调用信息的表中(此表的架构如下), InnoDB行级锁定用于在线程之间进行同步(Apache连接,实际上)请求相同的信息同时,这经常发生。这意味着多个线程可能正在等待一行锁定最多30秒,因为API调用可以花费那么长时间(但通常不会)。
  • 最重要的是,最重要的是在一般情况下一切都能正常运作。

也就是说,这是非常高度使用的表(每秒大约500个INSERT,许多SELECT,使用行级锁定)我正在运行DELETE查询:

CREATE TABLE `sales` (
  `sale_id` int(32) unsigned NOT NULL auto_increment,
  `start_time` int(20) unsigned NOT NULL,
  `end_time` int(20) unsigned default NULL,
  `identifier` char(9) NOT NULL,
  `zip_code` char(5) NOT NULL,
  `income` mediumint(6) unsigned NOT NULL,
  PRIMARY KEY  USING BTREE (`sale_id`),
  UNIQUE KEY `SALE_DATA` (`ssn`,`zip_code`,`income`),
  KEY `SALE_START` USING BTREE (`start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii ROW_FORMAT=FIXED

DELETE查询看起来像这样,并且每隔五分钟在cron上运行一次(我更喜欢每分钟运行一次):

DELETE FROM `sales` WHERE 
    `start_time` < UNIX_TIMESTAMP(NOW() - INTERVAL 30 MINUTE);

我已经将INT用于时间字段,因为很明显MySQL在使用DATETIME字段的索引时遇到了麻烦。

所以这就是问题:DELETE查询似乎在大多数情况下运行良好(可能是10次中的7次)。其他时候,查询很快完成,但MySQL似乎在一段时间后被扼杀了。我无法确切地证明它是正在运行的MySQL,但症状发生的时间肯定与运行此查询的时间一致。以下是症状,而一切都被呛到了:

  • 登录MySQL并使用SHOW FULL PROCESSLIST;,只运行了几个INSERT INTO个销售...个查询,通常有一百多个查询。这里的异常实际上是流程列表中缺少任何任务,而不是存在太多任务。似乎MySQL完全停止了连接。
  • 检查Apache服务器状态,Apache已达到MaxClients。所有主题都处于“发送回复”状态。
  • Apache开始使用大量系统时间CPU。负载平均值上升,我已经看到1分钟的平均负载高达100。这台机器的正常负载平均值大约为15.我看到它使用系统CPU(而不是用户CPU),因为我使用GKrellM来监控它。
  • top中,有许多Apache进程使用大量CPU。
  • 网站和API(当然由Apache提供服务)大部分时间都无法访问。有些请求会通过,但需要大约三到四分钟。其他请求在一段时间之后回复“无法通过/tmp/mysql.sock连接到MySQL服务器”错误 - 这与我在MySQL容量超过并且连接太多时获得的错误相同(只是它没有实际上说连接太多了。)
  • MySQL最多接受1024个连接,mysqltuner.pl报告“[!!]最高连接使用率:100%(1025/1024)”,这意味着它在一个点上可以处理得更多。通常在正常情况下,最多只有几百个并发MySQL连接。 mysqltuner.pl报告没有其他问题,如果有人想要,我很乐意粘贴输出。

最终,大约一两分钟后,事情就会自行恢复而不需要任何干预。 CPU使用率恢复正常,Apache和MySQL恢复正常运行。

那么,我该怎么办? :)我怎么能开始调查为什么会这样?我需要 DELETE查询由于各种原因而运行,为什么在运行时(但不是所有时间)事情都会变得疯狂?

1 个答案:

答案 0 :(得分:3)

很难的。这不是回应,而是头脑风暴的开始。

我想说,也许,删除时出现重新索引问题,在文档中我们可以找到“删除快速”,然后是“优化表”以尝试避免多索引合并。 / p>

另一种可能性,也就是说,删除时死锁链至少有一个其他线程,行锁可以暂停删除操作,删除操作可以暂停下一行锁。然后你有一个检测到的死锁或一个未检测到的死锁,因此发生了超时。你如何检测这种并发中止的异常?你重新运行交易吗?如果线程在同一个事务中执行了很多不同的行锁定,那么第一个死锁会影响越来越多的线程(流量堵塞)。

您是否尝试在删除交易中锁定表格?检查手册,在Innodb中锁定事务表的方式或在所有行上获得SHARE LOCK。也许你需要花一些时间才能拿到桌子,但是如果你的删除速度非常快,没有人会注意到你只为你拿了一张桌子。

现在即使您之前没有尝试过,也许删除正在做什么。检查this doc on implicit locks,您的删除查询应该使用start_time索引,所以我很确定您当前的删除不是锁定所有行(不完全确定,它们锁定已分析的行,而不仅仅是与行匹配的行)条件),但删除肯定是阻止插入。解释了具有事务执行删除的死锁的一些示例。祝好运!对我来说,了解所有锁定隔离影响为时已晚。

修改您可以尝试通过更新设置已删除= 1 来更改 DELETE ,然后执行低使用时间的真正删除(如果你有一些)。并更改客户端查询以检查此索引删除状态。