pdo事务真正卡人的地方在配置、错误捕获、边界控制和兜底逻辑四点:必须设errmode为exception、显式关闭autocommit、用try-catch包住全部操作并确保rollback、避免事务中混入ddl或长耗时操作。

PDO 事务不是“调个 beginTransaction 就完事”的功能,它真正卡人的地方在配置、错误捕获、边界控制和兜底逻辑四点。跳过这些直接抄示例,上线后大概率出数据不一致问题。
必须设 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
默认的 PDO::ERRMODE_SILENT 模式下,execute() 或 query() 失败只返回 false,不抛异常,try-catch 完全捕不到 SQL 错误(比如字段名错、表不存在、权限不足)。你得手动查 errorInfo(),但多数人根本不会查,结果就是“没报错,但数据没写进去”。
- 新建连接后立刻设置:$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
- 顺手加上 PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,避免每次 fetch() 都要写参数
- 不设这个,catch 只能捕到连接失败,捕不到 SQL 执行失败
事务三动作缺一不可:关 autocommit + beginTransaction + commit/rollback
PDO 默认是自动提交(autocommit = true),此时调 beginTransaction() 是空操作——SQL 依然立即生效。事务真正生效的前提是显式关闭自动提交。
- 构造时就配好:new PDO($dsn, $user, $pass, [PDO::ATTR_AUTOCOMMIT => false])
- 或连接后立刻设:$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, false)
- commit() 和 rollback() 都会隐式重置 autocommit 为 true,所以连续事务必须重新 beginTransaction()
- 漏掉任意一步(比如只写了 beginTransaction() 却没关 autocommit),事务就形同虚设
事务必须用 try-catch 包住全部操作,且 catch 中必须 rollback()
只写 beginTransaction() 和 commit(),不加异常处理,等于没做事务——一旦中间某步失败(如网络中断、磁盘满、SQL 语法错),后续代码不执行,commit() 永远不会走到,连接里就挂着一个未完成事务,可能锁表、阻塞其他请求,甚至拖垮数据库。
- 必须用 try 包住所有数据库操作,catch 中第一行就是 $pdo->rollback()
- 不要依赖 finally —— finally 里不能判断是否该 commit 还是 rollback,状态已丢失
- 如果事务跨多个函数调用,确保异常能透出到最外层的事务块,否则 rollback 会被跳过
别在事务里混用 query() 和 prepare(),尤其注意 DDL 和长耗时操作
query() 执行的是纯字符串 SQL,不走预处理,但如果你在事务中执行 DROP TABLE 或 ALTER TABLE,MySQL 会隐式提交当前事务(InnoDB 下 DDL 是自动提交的),导致后续 rollback() 失效。
- 事务内只做 DML(INSERT/UPDATE/DELETE/SELECT ... FOR UPDATE)
- 避免在事务中做文件读写、HTTP 请求、sleep() 等长耗时操作,会延长锁持有时间,增加死锁风险
- 不确定是否要复用语句时,统一用 prepare() + execute();简单无用户输入的语句(如 "SELECT COUNT(*) FROM user")可用 query(),轻量无开销
事务真正的复杂点不在语法,而在它和数据库引擎行为、PHP 异常传播、HTTP 生命周期的耦合。一个没被 catch 住的异常,一次没重置的 autocommit,一条混进来的 DDL,都可能让“本该原子”的操作变成半截残局。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











