Mysql - 序列化失败:1213尝试获取锁定时发现死锁;尝试重新启动事务,但查询不在事务中

时间:2018-01-06 09:22:22

标签: php mysql pdo

我收到此序列化失败:1213死锁在我开发和管理的系统上发现错误。

它出现在一个非常大且繁忙的桌子上。它有大约10米的行,可能在5个服务器上达到峰值时的40个查询/秒。

代码不使用交易。这张桌子上没有LOCK。该表是InnoDB并具有自动增量主键。

在以下查询期间发生错误:

UPDATE `Messages` SET Status='(new status)' WHERE `MessageID`='(ID)'

准备好查询,然后使用PHP和PDO执行。 99%的时间它运作得很好。

可能导致此问题的原因以及如何进行调试?

我使用的是PHP 7.0.22和MySQL Ver 14.14 Distrib 5.7.20。 MySQL在与PHP不同的服务器上运行并被复制,以便SELECT个查询在其中一个从服务器上运行。除了几个例外,它应该只在主数据库服务器上运行INSERTUPDATE

谢谢!

编辑:SHOW ENGINE InnoDB STATUS的结果:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-01-06 10:36:37 0x7fe606788700
*** (1) TRANSACTION:
TRANSACTION 492758175, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 2871743, OS thread handle 140625951213312, query id 167981859 sg-msg-02.company.local 10.32.80.3 myapp System lock
UPDATE Messages SET ThreadID=9 WHERE Status IS NULL AND ScheduleDate<now() AND ThreadID=0 ORDER BY FairQueuePos, ScheduleDate  LIMIT 50
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 63 page no 438651 n bits 184 index PRIMARY of table `Messaging`.`Messages` trx id 492758175 lock_mode X locks rec but not gap waiting
Record lock, heap no 115 PHYSICAL RECORD: n_fields 14; compact format; info bits 0
0: len 8; hex 800000000d3444e8; asc      4D ;;
1: len 6; hex 00001d5ee49e; asc    ^  ;;
2: len 7; hex 7f0000033e0110; asc     >  ;;
3: len 8; hex 800000000000048c; asc         ;;
4: len 12; hex 343437393532323333343436; asc 447952233446;;
5: len 4; hex 5a50a6b5; asc ZP  ;;
6: len 4; hex 5a50a6b4; asc ZP  ;;
7: len 4; hex 5a50a6b5; asc ZP  ;;
8: len 4; hex 5a50a6b4; asc ZP  ;;
9: len 4; hex 80000008; asc     ;;
10: len 1; hex 02; asc  ;;
11: len 30; hex 30313963613639662d393262662d346637372d623465632d353161363165; asc 019ca69f-92bf-4f77-b4ec-51a61e; (total 36 bytes);
12: len 2; hex 8021; asc  !;;
13: len 4; hex 80000005; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 492758174, ACTIVE 0 sec updating or deleting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 2871805, OS thread handle 140625927767808, query id 167981857 sg-msg-03.company.local 10.32.80.4 myapp updating
UPDATE Messages SET Status='Sent', RemoteMessageID='(redacted)', SentDate=now(), RouteID='8' WHERE MessageID='(redacted)' LIMIT 1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 63 page no 438651 n bits 184 index PRIMARY of table `Messaging`.`Messages` trx id 492758174 lock_mode X locks rec but not gap
Record lock, heap no 115 PHYSICAL RECORD: n_fields 14; compact format; info bits 0
0: len 8; hex 800000000d3444e8; asc      4D ;;
1: len 6; hex 00001d5ee49e; asc    ^  ;;
2: len 7; hex 7f0000033e0110; asc     >  ;;
3: len 8; hex 800000000000048c; asc         ;;
4: len 12; hex 343437393532323333343436; asc 447952233446;;
5: len 4; hex 5a50a6b5; asc ZP  ;;
6: len 4; hex 5a50a6b4; asc ZP  ;;
7: len 4; hex 5a50a6b5; asc ZP  ;;
8: len 4; hex 5a50a6b4; asc ZP  ;;
9: len 4; hex 80000008; asc     ;;
10: len 1; hex 02; asc  ;;
11: len 30; hex 30313963613639662d393262662d346637372d623465632d353161363165; asc 019ca69f-92bf-4f77-b4ec-51a61e; (total 36 bytes);
12: len 2; hex 8021; asc  !;;
13: len 4; hex 80000005; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 63 page no 443436 n bits 672 index StatusThreadSchedule of table `Messaging`.`Messages` trx id 492758174 lock_mode X locks rec but not gap waiting
Record lock, heap no 398 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: SQL NULL;
1: len 4; hex 5a50a6b4; asc ZP  ;;
2: len 2; hex 8021; asc  !;;
3: len 8; hex 800000000d3444e8; asc      4D ;;

*** WE ROLL BACK TRANSACTION (1)

编辑2:SHOW CREATE TABLE Messages

CREATE TABLE `Messages` (
`MessageID` bigint(20) NOT NULL AUTO_INCREMENT,
`UserID` bigint(20) NOT NULL DEFAULT '0',
`DestinationAddress` varchar(20) NOT NULL DEFAULT '',
`LastUpdate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`CreationDate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`SentDate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`ScheduleDate` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`RouteID` int(11) NOT NULL DEFAULT '0',
`Status` enum('Sending','Sent','Delivered','Undeliverable','Failed','Deleted','Deleting','Rejected','Unknown','Expired') DEFAULT NULL,
`RemoteMessageID` varchar(64) DEFAULT NULL,
`ThreadID` smallint(6) DEFAULT NULL,
`FairQueuePos` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`MessageID`),
KEY `IX_Relationship44` (`UserID`),
KEY `IX_Relationship67` (`RouteID`),
KEY `RemoteMessageID` (`RemoteMessageID`),
KEY `UserSchedule` (`ScheduleDate`),
KEY `StatusThreadSchedule` (`Status`,`ScheduleDate`,`ThreadID`),
KEY `UserStatus` (`UserID`,`Status`)
) ENGINE=InnoDB AUTO_INCREMENT=221559699 DEFAULT CHARSET=utf8 MAX_ROWS=4294967295

两个注意事项:较旧的消息从表中移出到存档表中(因此当前AUTO_INCREMENT处于此处)。还有第二个表,其中包含与此问题无关的消息的更多详细信息(例如消息的实际内容和其他元数据)。

另外我知道可能有太多的钥匙,我多年前加了它们,不敢搞砸它们。)

1 个答案:

答案 0 :(得分:1)

如果你真的不使用交易,它看起来像是外键问题。您可能拥有从RemoteMessageIDID(以及其他一些)的自引用外键。

第一个查询,

UPDATE Messages SET ThreadID=9 
WHERE Status IS NULL AND ScheduleDate<now() AND ThreadID=0 
ORDER BY FairQueuePos, ScheduleDate  LIMIT 50

可能会锁定第二个查询的行

UPDATE Messages SET Status='Sent', 
  RemoteMessageID='(redacted)', SentDate=now(), RouteID='8' 
WHERE MessageID='(redacted)' LIMIT 1

引用以及更新的行本身以及错误的时序可能会导致死锁。

除了一些明显的情况之外,为死锁添加快速修复并不容易,特别是在没有查看和分析完整的代码/逻辑的情况下。可以找到一些常规提示here

实际使用事务并使用

锁定引用的行可能会有所帮助
select * from Messages 
where MessageId = '<the Remote MessageID>' or MessageId = '<Id>' for update

它将尝试在使用它之前锁定它将使用的行,如果它不能,请等到它可以,而不是死锁(尽管它仍然可能导致死锁)。你可能需要类似其他外键的东西,尽管这可能是可疑的。

添加索引以支持您的第一个查询(可能至少包括ThreadID和/或Status,还有例如FairQueuePos补充)以减少数量也可能有所帮助由该查询锁定的行,以及减少执行该查询的时间。我猜第二个查询可能甚至没有触及ThreadID=0行,所以他们不应该再干涉了。但是,您可能还有其他任务和查询不能轻易分离。

如果没有任何帮助,只需重复死锁查询也是一个可行的解决方案。由于您的逻辑不要求您使用事务,因此不应该依赖于当前数据库状态,因此如果不经常发生这种情况会重复它会很好,这会减慢您的进程(太多)。虽然看起来有点难看,但您还需要针对其他错误执行此操作,例如丢失连接或超时等。

这似乎是一个批处理队列系统。如果您的开发不是太长,当然还有很多其他因素和要求,您可能需要查看其他现有的消息队列软件,这可能会让您的生活更轻松。