答案:pdo::begintransaction() 必须配合构造时设置pdo::errmode_exception才可靠。php 8.3未新增事务机制,但错误处理更严格:若未在new pdo()时传入pdo::attr_errmode => pdo::errmode_exception,begintransaction()失败不抛异常,commit()/rollback()可能静默失效;必须禁用模拟预处理、确保innodb引擎、避免readonly属性存运行时状态。

PDO::beginTransaction() 必须配合 PDO::ERRMODE_EXCEPTION 才真正可靠
PHP 8.3 没有新增事务机制,但对错误处理更严格:如果没在构造时就设好 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,beginTransaction() 失败(比如连接断开、权限不足)也不会抛异常,后续 commit() 或 rollBack() 可能静默失败,数据状态不可知。
常见错误现象:beginTransaction() 返回 true,但实际没进事务;执行 SQL 后直接生效,rollBack() 无效。
- 必须在 new PDO() 时传入错误模式配置,不能靠后置
setAttribute()—— 存在竞态风险 - 禁用模拟预处理:
PDO::ATTR_EMULATE_PREPARES => false,否则像CREATE TEMPORARY TABLE这类语句会触发隐式提交 - 确保数据库引擎是 InnoDB;MyISAM 不支持事务,
beginTransaction()看似成功,实则无效
mysqli::begin_transaction() 比手写 START TRANSACTION 更安全,但 autocommit(false) 不能省
很多人以为调用 mysqli::begin_transaction() 就自动关了自动提交,其实不是。它只是发一条 START TRANSACTION,前提是连接本身已处于手动提交模式。如果忘了先调 autocommit(false),那所有 SQL 仍会立刻提交。
常见错误现象:执行完 begin_transaction(),再执行 INSERT,查表发现数据已存在,rollback() 完全没效果。
- 必须先调
$mysqli->autocommit(false),再调$mysqli->begin_transaction() - 不推荐用
mysqli_query($conn, "START TRANSACTION")—— 它失败时不报错,返回false但容易被忽略;而begin_transaction()在 strict_types=1 下类型错误会提前中断,反而更容易暴露问题 - 事务结束后建议显式恢复:
$mysqli->autocommit(true),避免影响后续独立查询
事务中别用 readonly 属性存运行时状态
有人想用 readonly bool $committed = false 标记事务是否已提交,这在 PHP 8.3 会直接报 Fatal error: Cannot modify readonly property。只读属性只能在构造函数赋一次值,而 commit() 和 rollBack() 是运行时行为。
正确做法是用普通属性或局部变量记录状态,例如:
private bool $isCommitted = false; // … $this->isCommitted = true;
若需防误操作(比如重复 commit),可用私有方法 assertNotCommitted() 在关键步骤前校验,而不是依赖只读语义。
嵌套事务本质是保存点,MySQL 不支持真嵌套
PHP 层所谓“嵌套事务”只是通过 SAVEPOINT 实现的局部回滚。PHP 8.3 的 PDO 和 mysqli 都不提供原生嵌套 API,得自己管理保存点名和层级。
容易踩的坑:rollback() 默认回滚到最外层起点,不是上一层;要局部回滚,必须显式用 SAVEPOINT sp1 + ROLLBACK TO sp1。
事务生命周期必须在单次请求内闭环,不能跨 HTTP 请求或 CLI 进程保留。长时间未 commit/rollback 的事务会占用锁和连接资源,尤其在高并发下容易引发死锁或超时。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











