webman中db::begintransaction()失效主因是模型连接与事务连接不匹配,如user模型指定'mysql'连接而事务在'default'上开启;必须统一使用db::connection('mysql')开启、操作及回滚,并用\throwable捕获所有异常。

Webman里Db::beginTransaction()没生效?检查模型连接是否匹配
事务不生效最常见的原因是模型用了独立连接,但事务却在默认连接上开启。比如 User 模型设置了 protected $connection = 'mysql',而你调用的是 Db::beginTransaction()——它操作的是默认连接(通常是 'default'),两者根本不在一个连接实例上,自然无法控制该模型的写入。
必须显式指定连接:使用 Db::connection('mysql')->beginTransaction(),后续所有 save()、update()、insert() 也得走同一个连接实例,否则事务边界断裂。
- 模型未设
$connection时,Db::beginTransaction()才作用于默认连接 - 一旦模型指定了连接,事务、提交、回滚三者必须全部带上
->connection('xxx') - 多个模型跨不同连接(如
mysql和pgsql)时,PHP 无法实现跨库事务,只能分库各自加事务或改用消息队列补偿
捕获异常要用 \Throwable,不是 \Exception
Webman 常驻进程下,运行中可能触发 Error(如 FatalError、ParseError),而 \Exception 捕获不到它们。如果只写 catch(\Exception $e),事务会漏掉回滚,留下未提交的锁和脏数据。
正确写法是统一用 \Throwable:
Db::connection('mysql')->beginTransaction();
try {
$user = new User;
$user->name = 'test';
$user->save();
Db::connection('mysql')->commit();
} catch (\Throwable $e) {
Db::connection('mysql')->rollBack();
throw $e;
}
-
\Throwable是 PHP 7+ 的根接口,覆盖\Exception和所有Error类型 - Webman 日志组件(
webman/log)检测到未提交事务时,日志关键字是Uncommitted transactions,可快速定位漏 catch 的地方
SAVEPOINT 实现“局部回滚”,但不能嵌套事务
MySQL 不支持真正的嵌套事务,所谓“子事务”只是保存点(SAVEPOINT)。Webman 中若想在大事务里隔离某段逻辑失败不影响整体,得手动管理保存点。
示例:转账同时发通知,通知失败不应导致转账回滚:
Db::connection('mysql')->beginTransaction();
try {
// 转账主逻辑
Db::table('accounts')->where('id', 1)->decrement('balance', 100);
Db::table('accounts')->where('id', 2)->increment('balance', 100);
// 设保存点
Db::connection('mysql')->exec("SAVEPOINT notify_sp");
try {
// 发送通知(可能失败)
sendNotification();
} catch (\Throwable $e) {
Db::connection('mysql')->exec("ROLLBACK TO SAVEPOINT notify_sp");
// 继续执行后续操作,比如记日志
}
Db::connection('mysql')->commit();
} catch (\Throwable $e) {
Db::connection('mysql')->rollBack();
throw $e;
}
- 每个
SAVEPOINT名称必须唯一,重复名会被覆盖 -
ROLLBACK TO SAVEPOINT只能回退到当前事务内的保存点,不能跨beginTransaction()边界 - 保存点不释放锁,长时间挂起仍可能引发死锁
事务超时与长连接泄漏是 Webman 特有风险
Webman 是常驻进程,不像 FPM 每次请求新建连接。如果事务开启后没正常结束(比如忘了 commit() 或 rollBack()),这个连接会一直持有事务状态,直到超时或进程重启。MySQL 默认 innodb_lock_wait_timeout=50 秒,超时后报错 Lock wait timeout exceeded,但连接本身还在占用资源。
- 务必在
finally块或中间件中兜底检查事务状态(webman/log组件已内置该检测) - 避免在事务内做耗时操作(如 HTTP 请求、文件读写),应拆分为“事务内写 + 事务外异步处理”
- 配置 PDO 的
PDO::ATTR_TIMEOUT(如3秒)仅控制连接建立超时,不影响事务执行时长;真正要调的是 MySQL 的wait_timeout和innodb_lock_wait_timeout
\Throwable 漏捕——这两处一出问题,事务就形同虚设。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











