线上API偶现There is already an active transaction异常,ORM框架使用的是think-orm,仔细检查过代码,开启事务后,有成对出现的commit和rollback,没有嵌套事务,请问如何排查和解决。
目前想进一步确认:
1.开启事务后和提交事务后,使用$pdo->inTransaction()检查是否在事务中,都返回true,这是否正常,这样检查可靠吗?
2.某进程的请求出现There is already an active transaction异常,是不是跟其他进程下的事务无关,排查问题时只关注这个进程的日志即可?
精简后的method请求关键代码:
public function method() {
Log::debug("请求处理开始",['node' => php_uname('n'),'pid'=>getmypid()]);
$maxRetries = 3;
for ($i=0;$i<$maxRetries;$i++){
try {
$this->method1();
break;
} catch (NeedRetryException $e) {
Log::info('需要重试:'.$e->getMessage(),['node' => php_uname('n'),'pid'=>getmypid()]);
usleep(($i+1) * 100 * 1000);// 延迟后再进行后续处理
} catch (\Throwable $e) {
Log::critical($e->getMessage(),['node' => php_uname('n'),'pid'=>getmypid()]);
break;
}
}
Log::debug("请求处理完成",['node' => php_uname('n'),'pid'=>getmypid()]);
}
private function method1() {
Log::debug("下面开启事务",['node' => php_uname('n'),'pid'=>getmypid()]);
Db::startTrans();
$pdo = Db::connect()->getPdo();
Log::info("startTrans", [
'node' => php_uname('n'),
'pid' => getmypid(),
'pdo_id' => spl_object_id($pdo),
'inTransaction' => $pdo->inTransaction()
]);
try {
... 业务代码 ...
Db::commit();
Log::info("commit", [
'node' => php_uname('n'),
'pid' => getmypid(),
'pdo_id' => spl_object_id($pdo),
'inTransaction' => $pdo->inTransaction()
]);
} catch (NeedRetryException $e) {
Db::rollback();
Log::info("rollback", [
'node' => php_uname('n'),
'pid' => getmypid(),
'pdo_id' => spl_object_id($pdo),
'inTransaction' => $pdo->inTransaction()
]);
throw new NeedRetryException($e->getMessage());
} catch (\Throwable $e) {
Db::rollback();
Log::info("rollback", [
'node' => php_uname('n'),
'pid' => getmypid(),
'pdo_id' => spl_object_id($pdo),
'inTransaction' => $pdo->inTransaction()
]);
throw new Exception($e->getMessage());
}
return;
}
关键日志如下:
[2025-12-18 10:38:46] default.DEBUG: 请求处理开始 {"node":"xxx","pid":11010} []
[2025-12-18 10:38:47] default.DEBUG: 下面开启事务 {"node":"xxx","pid":11010} []
[2025-12-18 10:38:47] default.CRITICAL: There is already an active transaction {"node":"xxx","pid":11010} []
[2025-12-18 10:38:47] default.DEBUG: 请求处理完成 {"node":"xxx","pid":11010} []
[2025-12-18 10:38:47] default.DEBUG: 请求处理开始 {"node":"xxx","pid":11010} []
[2025-12-18 10:38:48] default.INFO: 下面开启事务 {"node":"xxx","pid":11010} []
[2025-12-18 10:38:48] default.INFO: startTrans {"node":"xxx","pid":11010,"pdo_id":93,"inTransaction":true} []
[2025-12-18 10:38:48] default.INFO: commit {"node":"xxx","pid":11010,"pdo_id":93,"inTransaction":true} []
[2025-12-18 10:38:50] default.DEBUG: 请求处理完成 {"node":"xxx","pid":11010} []
CentOS Linux release 8.1
php 8.2.16
workerman/webman-framework v1.5.16
workerman 4.1.15
webman/think-orm v1.1.1
可能是有事务没提交,安装 webman/log 日志里会记录哪个请求没提交事务。
进程间是完全隔离的,进程间的事务不会有这种问题。
好的,谢谢!我试试。
安装 webman/log要求升级webman版本,我把webman版本从v1.5.16直接升级到v2.1.4后,发现定时任务的数据查询操作报错:
请问是我升级方式不对吗,是否需要v1.5->v1.6->2.0->2.1逐版本升级?
尝试了单独基于v2.1版本框架创建新项目,测试定时任务中使用数据库没发现问题。对比两个项目的基本框架部分,发现有部分差异,比如:创建的新项目process在app目录里面,从v1.5升级到v2.1的项目里没改变process的路径。
https://www.workerman.net/doc/webman/upgrade/2-1.html
是按这个文档升级的,发现异常是在启动后,达到心跳检测间隔heartbeat_interval的时候抛出,但是我不清楚根本原因。
达到心跳检测间隔heartbeat_interval之前的查询正常的
升级文档里的这个执行了没
执行完restart重启
升级执行过composer require -W webman/think-orm:~2.1,但是本地是Windows环境,项目没启动时进行的升级,没执行restart重启,是执行的php windows.php启动的项目。
看下 config下是不是有两个配置文件,think-orm.php和thinkorm.php ,保留一个,删除另外一个
只有一个thinkorm相关配置,对比过新建的项目,配置是think-orm.php,也尝试过只保留think-orm.php,配置都是生效的,能连上数据库数据查询正常。
目前发现本地Windows环境单独基于v2.1版本框架创建的新项目,测试定时任务中使用本地数据库没问题,但是使用云数据库会复现心跳检测时报错。
但是报错有点不同:
新版本数据库命名空间有一点区别
https://www.workerman.net/doc/webman/db/thinkorm.html
好的,谢谢!我改一下。
还是不行,我把新建的demo项目公开出来 https://gitee.com/DreamWakex/webman-demo
目前发现代码不动的情况下,配置使用本地MySQL数据库、阿里云RDB MySQL数据库正常,配置使用阿里云PolarDB-X分布式数据库时才有问题,所以可以说这个问题跟升级后的代码无关了。
分享一下进展。重写连接池等代码,加调试日志,配合wireshark抓包,最终发现连接PolarDB-X分布式数据库时,心跳检测报错的根本原因是框架执行
SELECT 1时报错:#HY000[1aff133d31403000][11.246.52.178:3054][test_promotion]Prepare does not support sql: select 1,在重写的DbManager中心跳检测部分把检测的SQL改成select 1 as x解决。更新 webman/think-orm 到2.1.9试下,做了兼容
好的
我遇到过 是不是用的云数据库
你是不是用的更新操作 save操作?
是连的云数据库,但不是在执行数据库操作的时候报错,是在心跳检测的时候报错。参考如下错误的
think\db\PDOConnection->getPDOStatement('select 1', Array, false, false):There is already an active transaction 我说的是这个东西
哦哦,确实是有不少地方都是用的模型的save。也发现了save方法的源码实现会自动包裹事务,但是源码也有异常回滚,没看出save调用有什么影响。
你把save 改成 update 试试, 另外 检查是否有 报错后未rollback的代码
报错后未rollback检查过几遍,肉眼没发现问题,还是等升级新版框架后运行正常后查看 webman/log 日志。你之前遇到这个问题是通过save 改成 update解决的吗?改update还是模型的方式调用update方法吧,$model->save()改成$model->update(),而不是Db::where()->update()。
就模型调用 update 就行, save 方法是会比对修改的字段 原数据和修改数据是否一致,不一致才会更新,但是在云数据库的情况下,就会有问题,我们当时用的polardb, 读写分离是在云上控制的 会有这个问题
好的,感谢!我试试。