如何区分PDOException中的语法错误、死锁及连接错误以优化处理?
区分PDOException错误类型并实现重试逻辑
要实现PDO异常的类型区分和针对性重试,核心是利用PDOException的两个关键属性:$exception->getCode()(SQLSTATE标准错误码)和$exception->errorInfo[1](数据库原生错误码,比如MySQL的具体错误编号)。以下是具体实现方案:
1. 明确不同错误对应的标识码
以MySQL为例,常见需区分的错误码如下:
- 死锁(Deadlock):原生错误码
1213,SQLSTATE码40001 - 连接类错误:连接超时
2002、连接丢失2013、连接数超限1040,SQLSTATE多为HY000 - 语法错误:原生错误码
1064,SQLSTATE码42000 - 参数/数据错误:字段值过长
1406,SQLSTATE码22001
2. 代码实现示例
先封装错误类型判断函数,再在事务逻辑中加入重试机制:
// 判断是否为可重试的错误类型 function isRetriablePDOError(PDOException $e) { // 可重试的MySQL原生错误码 $retryNativeCodes = [ 1213, // 死锁 2002, // 连接超时 2013, // 查询过程中连接丢失 1040, // 数据库连接数过多 ]; // 可重试的SQLSTATE码 $retrySqlStates = [ '40001', // 死锁标准码 ]; // 优先用原生错误码判断(更精准) if (in_array($e->errorInfo[1], $retryNativeCodes)) { return true; } // 补充SQLSTATE码判断 if (in_array($e->getCode(), $retrySqlStates)) { return true; } // 针对HY000这类通用连接错误,需排除非重试场景 if ($e->getCode() === 'HY000') { $nonRetryHy000Codes = [1064, 1406]; return !in_array($e->errorInfo[1], $nonRetryHy000Codes); } return false; } // 业务事务处理+重试逻辑 $maxRetries = 3; $retryCount = 0; $operationSuccess = false; do { try { $pdo->beginTransaction(); // 执行你的业务SQL:插入/更新/删除 $stmt = $pdo->prepare("INSERT INTO target_table (col1, col2) VALUES (:val1, :val2)"); $stmt->execute([':val1' => $val1, ':val2' => $val2]); $pdo->commit(); $operationSuccess = true; } catch (PDOException $e) { // 回滚事务 $pdo->rollBack(); // 记录错误详情 error_log( sprintf( "PDO Error: %s | Native Code: %s | SQLSTATE: %s", $e->getMessage(), $e->errorInfo[1], $e->getCode() ) ); if (isRetriablePDOError($e) && $retryCount < $maxRetries) { $retryCount++; // 重试前加入短暂延迟,降低再次死锁概率 usleep(500000); // 500毫秒 } else { // 非重试类错误(语法/参数错误),直接跳过 error_log("Non-retriable error, skipping current operation."); break; } } } while (!$operationSuccess && $retryCount <= $maxRetries);
3. 关键注意事项
- 不同数据库的错误码存在差异,比如PostgreSQL死锁的SQLSTATE是
40P01,需根据实际使用的数据库调整错误码列表 - 不要盲目重试
HY000类型错误,该SQLSTATE包含大量非重试场景,必须结合原生错误码过滤 - 限制重试次数,避免无限循环占用资源
- 语法错误、参数越界属于代码逻辑问题,重试无意义,直接跳过即可
内容的提问来源于stack exchange,提问作者inf3rno
相关产品推荐
相关产品推荐

