我收到此序列化失败: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
个查询在其中一个从服务器上运行。除了几个例外,它应该只在主数据库服务器上运行INSERT
和UPDATE
。
谢谢!
编辑: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
处于此处)。还有第二个表,其中包含与此问题无关的消息的更多详细信息(例如消息的实际内容和其他元数据)。
另外我知道可能有太多的钥匙,我多年前加了它们,不敢搞砸它们。)
答案 0 :(得分:1)
如果你真的不使用交易,它看起来像是外键问题。您可能拥有从RemoteMessageID
到ID
(以及其他一些)的自引用外键。
第一个查询,
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
行,所以他们不应该再干涉了。但是,您可能还有其他任务和查询不能轻易分离。
如果没有任何帮助,只需重复死锁查询也是一个可行的解决方案。由于您的逻辑不要求您使用事务,因此不应该依赖于当前数据库状态,因此如果不经常发生这种情况会重复它会很好,这会减慢您的进程(太多)。虽然看起来有点难看,但您还需要针对其他错误执行此操作,例如丢失连接或超时等。
这似乎是一个批处理队列系统。如果您的开发不是太长,当然还有很多其他因素和要求,您可能需要查看其他现有的消息队列软件,这可能会让您的生活更轻松。