【发布时间】:2009-10-12 12:58:06
【问题描述】:
在 PHP 中处理 MySQL 死锁的最佳实践是什么?我是否应该将所有数据库调用包装在 try{}catch{} 块中并从数据库中查找死锁错误代码?然后我是否再次重新发出整个事务(我假设回滚失败的那个)?
【问题讨论】:
标签: php mysql transactions
在 PHP 中处理 MySQL 死锁的最佳实践是什么?我是否应该将所有数据库调用包装在 try{}catch{} 块中并从数据库中查找死锁错误代码?然后我是否再次重新发出整个事务(我假设回滚失败的那个)?
【问题讨论】:
标签: php mysql transactions
死锁返回错误1213,您应该在客户端处理该错误
请注意,死锁和锁等待是不同的东西。在僵局中,没有“失败”的交易:他们都是有罪的。无法保证会回滚哪一个。
死锁发生在这样的场景中:
UPDATE t_first -- transacion 1 locks t_first
SET id = 1;
UPDATE t_second -- transaction 2 locks t_second
SET id = 2;
UPDATE t_second -- transaction 1 waits for transaction 2 to release the lock on t_second
SET id = 2;
UPDATE t_first -- transaction 2 waits for transaction 1 to release the lock on t_first. DEADLOCK
SET id = 2;
您确定不会将其与锁定等待混淆吗?
每当一个事务试图锁定已被另一个事务锁定的资源时,就会发生锁定等待。
在上面的示例中,锁等待发生在步骤 3。
由于这是一种正常情况(与死锁不同),可以通过提交或回滚持有锁的事务从外部解决,InnoDB 不会尝试回滚持有锁的事务。
相反,它只会在超时发生后取消试图获取锁的语句。
默认超时时间为50 秒,并使用innodb_lock_wait_timeout 设置。
失败的语句(试图获取锁的语句)将返回错误1205。
【讨论】:
我想引用 MySQL 的How to Cope with Deadlocks这些温暖的话
如果由于死锁而失败,请随时准备重新发出事务。死锁并不危险。请再试一次。
这可以通过这样的模式来实现:
for ($i = 3; true; $i--) {
$pdo->beginTransaction();
try {
// Do the unit of work
$pdo->commit();
break;
} catch (\PDOException $e) {
$pdo->rollback();
if ($i <= 0) {
throw $e;
}
}
}
【讨论】: