当前位置:首页 > 文章列表 > 文章 > php教程 > PHP PDO 事务异常时如何避免自动提交半成品

PHP PDO 事务异常时如何避免自动提交半成品

来源:17golang原创 2026-09-12 18:48:24 0浏览 收藏

订单保存经常不是一条 SQL:先写订单主表,再写明细、扣减库存,最后记录操作日志。只要中间一步失败,前面的写入就不能留在数据库里。PHP PDO 的正确做法是把这些语句放进显式事务,成功路径只在最后调用 commit(),异常路径调用 rollBack(),不要依赖“某条 SQL 报错后数据库会自动替你收尾”。

要点速览
  • 连接后启用 PDO::ERRMODE_EXCEPTION,让失败可被同一个 catch 捕获。
  • beginTransaction()commit() 是成功边界,任何异常都应进入回滚分支。
  • 事务能否真正回滚还取决于驱动、表引擎和是否执行了会隐式提交的 DDL。

先把“半成品”变成一个可回滚单元

PDO 新建连接时通常处于自动提交模式,每条写语句可能拥有自己的隐式事务。需要原子性时,必须显式调用 beginTransaction()。下面用一个订单和库存的简化场景说明边界;示例中的表名、数量和异常都是演示数据,不代表已经在本机执行。

 PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
);

$pdo->beginTransaction();

try {
    // 主表和明细必须处在同一个事务里,避免只生成订单头。
    $order = $pdo->prepare(
        'INSERT INTO orders (user_id, total_amount) VALUES (:user_id, :total)'
    );
    $order->execute(['user_id' => 42, 'total' => 199.00]);
    $orderId = (int) $pdo->lastInsertId();

    // 库存不足或 SQL 失败时,异常会跳到 catch,不会提前提交订单。
    $stock = $pdo->prepare(
        'UPDATE inventory SET quantity = quantity - :qty
         WHERE sku = :sku AND quantity >= :qty'
    );
    $stock->execute(['qty' => 1, 'sku' => 'KB-100']);
    if ($stock->rowCount() !== 1) {
        throw new RuntimeException('库存不足,取消订单');
    }

    // 只有全部写操作成功,才把半成品变成正式数据。
    $pdo->commit();
} catch (Throwable $error) {
    // 事务可能已被数据库或驱动终止,先判断活动状态再回滚。
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    // 生产环境记录内部错误,返回给用户的消息应保持简洁。
    throw $error;
}
?>
PHP PDO 事务示意中的异常模式、beginTransaction 和订单库存写操作边界
图1:PHP PDO 事务的操作示意图,展示异常模式、事务入口和订单库存写操作如何被同一边界包住。

这里把 beginTransaction() 放在 try 外,是为了让“启动事务失败”和“事务内 SQL 失败”分开理解。实际项目也可以把它放进更大的 try,但捕获后不要无条件回滚:没有活动事务时,rollBack() 本身可能再次抛出异常。

异常模式解决的是“能不能回滚”,不是业务判断

PDO::ERRMODE_EXCEPTION 让执行失败抛出 PDOException,从而不会因为忘记检查 execute() 返回值而继续走到提交。它并不会替你判断库存是否足够、金额是否正确,也不会把业务规则失败自动变成异常,所以示例中仍然主动抛出 RuntimeException

写入代码建议保持一个清晰的节奏:准备参数、执行语句、检查业务结果;所有成功条件满足后才提交。不要在每一步后分别调用 commit(),否则后续失败只能回滚后半段,主表和明细就会失去原子性。

三个容易让回滚失效的边界

边界表现处理方式
表引擎或驱动不支持事务方法看似成功,数据却没有按预期回滚确认 PDO 驱动和数据库表引擎;MySQL 写表通常使用 InnoDB
事务外写入某条 SQL 在 beginTransaction() 之前执行把所有相关写操作移到事务入口之后
事务内执行 DDLCREATE TABLE、DROP TABLE 等可能触发隐式 COMMIT把结构变更和业务写入拆开,避免把 DDL 当作可回滚业务步骤

PDO 只能在驱动层判断是否支持事务,不能保证当前数据库条件一定具备回滚能力;例如 MySQL 的非事务表可能让 beginTransaction() 返回成功,但写入仍无法像 InnoDB 那样撤销。另一个常见误区是把“脚本结束自动回滚”当成业务设计。它只能作为异常退出时的安全兜底,成功路径仍应显式提交,异常路径仍应显式回滚和记录。

PHP PDO 事务结果示意中展示 commit 成功、rollBack 异常分支和 DDL 隐式提交警告
图2:PHP PDO 事务的结果示意图,对比统一提交、异常回滚和 DDL 隐式提交三种结果状态。

上线前用三组场景确认边界

  1. 成功场景:订单、明细和库存都满足条件,检查三张相关表都留下对应记录。
  2. 异常场景:在库存更新后人为抛出异常,检查订单和明细没有残留,库存也没有被扣减。
  3. 重复提交场景:故意再次调用 commit() 或重复进入事务,确认应用能记录错误,而不是静默产生第二份数据。

测试时不要只看 PHP 日志里的异常。还要在数据库连接断开后查询结果,并确认连接复用前已经结束事务。若使用多个数据库连接,PDO 事务只约束当前连接,不能自然覆盖另一个连接上的写操作。

常见问题

PDO 抛出异常后还需要手动 rollBack() 吗?

需要。异常模式负责把控制流送进 catch,不会替你完成业务层清理;在确认 inTransaction() 为真后显式回滚更清楚。

只调用 rollBack() 能撤销事务开始前的 INSERT 吗?

不能。回滚只作用于当前 beginTransaction() 之后、提交之前的操作,事务开始前的写入已经按自动提交处理。

为什么 MySQL 回滚了但数据仍在?

先检查表是否使用支持事务的引擎,再检查中间是否执行了会隐式提交的 DDL;最后确认所有语句使用的是同一个 PDO 连接。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go build -ldflags 传入空字符串为什么被截断Go build -ldflags 传入空字符串为什么被截断
上一篇
Go build -ldflags 传入空字符串为什么被截断
运营团队用Lovart做多渠道活动素材,哪些环节仍要人工把关?
下一篇
运营团队用Lovart做多渠道活动素材,哪些环节仍要人工把关?
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    104次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    21次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    31次使用
  • AGI-Eval大模型评测平台:权威榜单、数据集与人机协同评测方案
    AGI-Eval
    AGI-Eval是由上海交大等高校联合发布的大模型评测社区,提供公正透明的LLM能力榜单、多领域评测集及Data Studio数据服务,助力AI模型性能评估与NLP科研开发。
    22次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    259次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码