高峰期下单、库存扣减或批量更新时,日志出现 SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock。死锁不是简单的“数据库太慢”,而是两个事务以不同顺序等待彼此持有的锁。MySQL 会回滚其中一个事务解除循环。
事故发生时先保存现场
mysql -e "SHOW ENGINE INNODB STATUS\G" > /tmp/innodb-status.txt
mysql -e "SELECT * FROM information_schema.innodb_trx\G" > /tmp/innodb-trx.txt
mysql -e "SHOW FULL PROCESSLIST" > /tmp/processlist.txt
grep -Rni 'Deadlock found|SQLSTATE[40001]' var/log | tail -n 100
SHOW ENGINE INNODB STATUS 只保留最近一次死锁,等故障过去再查往往已被覆盖。生产环境可开启适当的 deadlock 日志并配置轮转。
阅读 LATEST DETECTED DEADLOCK
- 记录两个事务正在执行的 SQL;
- 记录已持有和正在等待的 lock mode;
- 记录涉及的索引、记录和修改行数;
- 确认最终被回滚的是哪个事务;
- 追到发起 SQL 的 Cron、Consumer、API 或后台操作。
仅凭表名停掉所有消费者会扩大影响。要从时间、线程和业务唯一键把数据库现场与 Magento 日志对应起来。
事务范围过大会放大冲突
$connection->beginTransaction();
try {
$this->resourceA->save($a);
// 数据库事务中不要调用外部 API 或处理大文件
$this->resourceB->save($b);
$connection->commit();
} catch (Throwable $e) {
$connection->rollBack();
throw $e;
}
把外部接口、邮件与大文件处理移出事务。多个业务路径更新同一组实体时统一顺序,例如总是先订单、再库存,避免另一条路径反向加锁。
缺少索引会锁住更多记录
EXPLAIN UPDATE custom_table SET status=1
WHERE external_id='abc' AND store_id=2;
SHOW INDEX FROM custom_table;
增加索引前先在预发布验证执行计划和写入成本,不要在高峰期直接 ALTER 大表。
有限重试必须以幂等为前提
for ($attempt = 1; $attempt <= 3; $attempt++) {
try {
$this->operation->execute($idempotencyKey);
break;
} catch (MagentoFrameworkDBAdapterDeadlockException $e) {
if ($attempt === 3) { throw $e; }
usleep(random_int(50000, 200000) * $attempt);
}
}
纯内部状态更新可有限重试;已经调用支付、发券、发货或 ERP 的流程,必须先用唯一键确认外部动作是否发生。上线后同时观察死锁次数、事务耗时、队列重试和订单完整性,不能把异常吞掉或无限重试。
用业务键核对被回滚事务
数据库选择的 victim 可能是客户下单,也可能是后台批量任务。根据订单号、SKU、外部请求 ID 检查事务是否已经部分产生消息、历史记录或外部调用。若数据库回滚但消息已投递,就必须依赖消费者幂等性阻止重复处理。
对于周期性批量任务,可按主键排序并分小批提交,减少一次事务持有的锁数量,同时避免多个 worker 处理重叠范围。
把死锁频率与流量放在一起看
单次死锁可能是正常并发竞争,持续增长则需要工程修复。按分钟汇总错误次数,并与下单量、消费者并发数、批量任务开始时间和数据库 CPU/IO 对齐。若每次索引任务启动就出现同一组表冲突,可以调整任务窗口或拆分批次,但仍要统一代码的锁顺序。
grep 'Deadlock found' var/log/*.log | cut -c1-16 | sort | uniq -c | tail -n 30
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks';"
mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_running';"
修改后保留一段时间的基线数据,确认死锁下降不是因为吞吐也下降。性能优化必须同时观察成功订单数与处理延迟。

