PHP PDO添加重试逻辑解决死锁后遇活跃事务错误求助
解决PDO持久连接下事务重试导致的「已有活跃事务」错误
问题根源
- 持久化连接的事务残留:PDO持久连接会在进程间复用,若某连接上的事务因异常未正确回滚,后续复用该连接的脚本调用
beginTransaction()时就会触发报错。 - 重试逻辑的事务清理漏洞:当前代码仅在非死锁异常时回滚事务,死锁触发重试时直接
continue,未回滚失败的事务,导致事务一直处于活跃状态,下一次循环执行beginTransaction()就会报错。 - 多独立脚本的连接复用风险:
prepare.php通过shell_exec启动40个独立进程,这些进程复用连接池中的连接时,可能继承了前一个进程未关闭的事务。
修复方案
1. 完善重试逻辑的事务清理
无论是否是死锁异常,事务失败后必须先回滚再重试或退出。修改插入脚本的异常处理:
$retryAttempts = 3; $retryDelay = 100000; // 微秒(0.1秒) for ($attempt = 1; $attempt <= $retryAttempts; $attempt++) { try { // 先检查并清理当前连接的活跃事务 if ($pdo_conn->inTransaction()) { $pdo_conn->rollback(); } $pdo_conn->beginTransaction(); $stmt = $pdo_conn->prepare($insert_query); $stmt->execute(); $pdo_conn->commit(); break; } catch (PDOException $e) { // 任何异常都先回滚事务 if ($pdo_conn->inTransaction()) { $pdo_conn->rollback(); } if (($e->getCode() == "1213" || $e->getCode() == "40001") && $attempt < $retryAttempts) { echo "触发死锁,进行第{$attempt}次重试" . PHP_EOL; usleep($retryDelay); continue; } else { echo "事务失败:" . $e->getMessage() . PHP_EOL; break; } } }
2. 优化持久连接的使用
如果业务允许,关闭持久连接(移除PDO::ATTR_PERSISTENT => true),彻底避免连接复用带来的问题。若必须使用持久连接,在初始化连接后强制清理残留事务:
// connection.php 初始化连接后添加 if ($pdo_conn->inTransaction()) { $pdo_conn->rollback(); }
3. 独立脚本的连接隔离
每个scripts_{$i}.php完成操作后主动销毁连接,确保连接池中的连接无事务残留:
// 插入操作完成后添加 $pdo_conn = null;
内容的提问来源于stack exchange,提问作者Parvez Hossain
相关产品推荐
相关产品推荐

