事务没生效,先确认pdo是否关闭自动提交:默认autocommit为true,begintransaction()被忽略;须显式设$pdo->setattribute(pdo::attr_autocommit, false),并用try-catch配对commit/rollback。

PHP中事务没生效?先确认PDO是否在自动提交模式
事务不回滚、commit()后数据仍丢失,大概率是PDO连接默认开启了自动提交。PHP的PDO默认行为是PDO::ATTR_AUTOCOMMIT设为true,此时beginTransaction()看似成功,实则被忽略。
- 创建PDO实例后必须显式关闭自动提交:
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, false) - 或在构造时传入选项:
new PDO($dsn, $user, $pass, [PDO::ATTR_AUTOCOMMIT => false]) - 检查当前状态可用:
$pdo->getAttribute(PDO::ATTR_AUTOCOMMIT),返回1表示开启,0才是关闭 - 注意:MySQL的
MyISAM引擎不支持事务,务必用InnoDB
调用beginTransaction()后必须配对commit()或rollback()
beginTransaction()只是启动事务,并不持久化;若脚本结束前既没commit()也没rollback(),PDO会自动回滚——但这个“自动”不可靠,尤其在异常未被捕获、或exit()/die()提前终止时。
- 务必用
try...catch包裹事务块,catch里必须调用$pdo->rollback() -
commit()成功后,PDO会自动恢复自动提交状态(如果之前是true),但不保证后续操作仍在事务上下文中 - 不要在事务中混用
mysql_query()等旧扩展函数,它们与PDO事务无关 - 示例关键结构:
try { $pdo->beginTransaction(); $pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1"); $pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2"); $pdo->commit(); // 成功才提交 } catch (Exception $e) { $pdo->rollback(); // 出错立刻回滚 throw $e; }嵌套事务在PHP/PDO中不被原生支持
MySQL本身不支持真正的嵌套事务(SAVEPOINT除外),而PDO更没有
beginTransaction()的嵌套计数机制。多次调用beginTransaction()不会报错,但第二次调用实际无效,且commit()会提交整个外层事务——容易造成逻辑误判。- 避免手动实现“伪嵌套”,比如在函数内重复调用
beginTransaction() - 如需部分回滚,改用
SAVEPOINT:$pdo->exec("SAVEPOINT sp1")和$pdo->exec("ROLLBACK TO sp1") - 注意:
SAVEPOINT名需唯一,且不能跨连接复用 - 某些ORM(如Laravel)封装了嵌套事务语义,但底层仍是
SAVEPOINT,非PDO原生能力
事务期间的锁等待与超时容易被忽略
事务中的写操作(
UPDATE/INSERT)会加行锁,若另一连接正持有该行锁,当前事务会阻塞,直到超时(MySQL默认innodb_lock_wait_timeout=50秒),然后抛出SQLSTATE[HY000]: General error: 1205 Deadlock found或超时错误。- 长时间事务增加锁冲突概率,应尽量缩短事务内代码执行时间
- 避免在事务中做HTTP请求、文件读写、sleep()等耗时操作
- 可通过
SHOW ENGINE INNODB STATUS查最近死锁详情 - 若业务允许,考虑用
SELECT ... FOR UPDATE NOWAIT(MySQL 8.0+)立即失败而非等待
事务不是银弹,它解决的是单次请求内的原子性;跨服务、跨库、异步场景下,得靠补偿、消息队列或Saga模式。PDO事务的边界非常清晰——只管当前连接、当前会话、当前SQL执行流。漏掉
rollback()、连错引擎、或在锁住的行上卡住几秒,都可能让一致性变成幻觉。 - 避免手动实现“伪嵌套”,比如在函数内重复调用
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











