php - Mysql - 序列化失败 : 1213 Deadlock found when try to get lock; try restarting transaction, 但查询不在事务中

标签 php mysql pdo

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

它发生在一个非常大且繁忙的表上。它有大约 1000 万行,峰值时每秒可能从五台服务器获得超过 40 个查询。

代码不使用事务。这张 table 上没有锁。该表是 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 查询在其中一个从服务器上运行。除了几个异常(exception),它应该只在主数据库服务器上运行 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 当前所在的位置)。还有第二个表包含与此问题无关的有关消息的更多详细信息(例如消息的实际内容和其他元数据)。

我也知道可能是key太多了,很多年前加的,不敢乱动:)

最佳答案

如果你真的不使用事务,看起来像是外键的问题。您可能有一个从 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 的行,所以它们不应该再干扰了。但是您可能还有其他任务和查询不容易分开。

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

这好像是一个批处理队列系统。如果您的开发还不太深入,当然还取决于许多其他因素和要求,您可能想看看其他现有的消息队列软件,这可能会让您的生活更轻松一些。

关于php - Mysql - 序列化失败 : 1213 Deadlock found when try to get lock; try restarting transaction, 但查询不在事务中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48125942/

相关文章:

php - 生成随机 ENUM 值 [PHP, MySQL]

mysql - 按名称排序,然后按父级排序

MYSQL 分组查询

php - 使用 PDO 准备语句和特定年份的占位符,我无法回应任何结果

php - 选择并取消链接 3 个表上的文件

php - curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this ,'readHeader' )) 不工作

php - 使用AJAX调用PHP页面问题

php - WooCommerce 购物车更改后更新短代码显示

php - Dreamweaver PHP mySQL 插入到 WordPress wpdb 转换

PHP - rowCount() 给出相同的值,无论字符串是否存在