ThinkPHP 8 重构事务机制,实现连接专属绑定、原生 SAVEPOINT 嵌套、非 DML 语句拦截及协程隔离;要求显式连接实例、禁用静态快捷方法,并依赖 PDO 真预处理与 MySQL ≥ 5.6。

ThinkPHP 8 在数据库事务处理机制上不是简单增强,而是重构了底层契约与运行逻辑——它把事务从“可选工具”升级为“上下文感知的资源调度行为”,尤其在连接复用、异常传播、嵌套控制和协程兼容四方面有实质性改进。
连接池与事务生命周期绑定更严格
TP5 中事务开启(Db::startTrans())后,底层仍可能复用非事务状态的连接;TP8 要求事务必须运行在显式获取的连接实例上,且该连接会自动标记为“事务专属”,直到 commit() 或 rollback() 后才归还至连接池。若中途未正确结束事务,连接会被直接丢弃,避免脏状态污染后续请求。
- TP5 写法:
Db::startTrans(); Db::table('user')->update([...]); Db::commit();→ 连接可能被其他非事务查询复用 - TP8 推荐写法:
$db = Db::connect('default'); $db->startTrans(); $db->table('user')->update([...]); $db->commit();→ 事务全程绑定同一连接实例
嵌套事务支持 true savepoint 语义
TP5 的嵌套事务本质是计数器模拟,startTrans() 多次调用仅增加层级,rollback() 一次就回滚全部;TP8 基于 PDO 的 SAVEPOINT 原生实现,支持按名称创建/回滚指定保存点,真正实现局部回滚。
- ✅ 正确示例:
$db->execute('SAVEPOINT sp1'); ... $db->execute('ROLLBACK TO SAVEPOINT sp1'); - ⚠️ TP5 不支持 savepoint 语法,硬写会报错;TP8 在启用事务后自动识别并透传给 PDO
- 注意:需 MySQL ≥ 5.6 且配置中
'params' => [PDO::ATTR_EMULATE_PREPARES => false]
事务中执行非事务语句会被主动拦截
TP5 对事务内执行 SELECT 或 SHOW 等语句无限制;TP8 在事务活跃时,若调用 Db::query() 执行非 DML 语句(如 SELECT),会抛出 think\db\exception\TransactionException,强制开发者明确区分读写意图。
立即学习“PHP免费学习笔记(深入)”;
- 这是为了防止“事务中混入非一致性读”,比如在更新用户余额时意外执行未加锁的
SELECT COUNT(*) - 如确需读操作,应改用
$db->transaction(function ($db) { return $db->table('log')->select(); });—— 此方式内部自动处理读上下文
协程环境下事务自动隔离(Swoole 场景)
TP5 完全不感知协程;TP8 在 Swoole + enable_coroutine=1 下,每个协程拥有独立的事务连接上下文,即使并发执行多个事务,也不会因共享连接导致 commit 错乱或 rollback 波及他人。
- 前提:必须使用
Db::connect()获取连接,不能用Db::table()等静态快捷方法 - 验证方式:在协程中打印
spl_object_id($db->getPdo()),多个并发事务返回不同 ID - FPM 模式下此特性不生效,事务行为与 TP5 类似,只是校验更严



















