事务提交后查到旧数据,主因是查询未绑定同一session或未设readConcern: "majority";PHP驱动中所有find操作必须显式传入session参数,否则为普通读,不参与事务隔离。

事务提交后查到旧数据,不是事务没生效,而是你查的压根不是事务写入的那个文档——最常见原因是查询条件没命中刚更新的文档,或者读操作没绑定事务会话。
PHP 驱动里不传 session 参数,查询就脱离事务上下文
PHP 的 MongoDB 驱动(mongodb/mongodb)要求每个操作显式传入 session,否则默认走普通读,不参与事务隔离。哪怕你在 withTransaction() 回调里写了 find(),如果漏传 session,它就变成一个独立的、非事务的读请求。
-
collection->find(['status' => 'done'])→ 普通读,可能看到事务前状态 -
collection->find(['status' => 'done'], ['session' => $session])→ 事务内读,能看到自己刚写的变更
注意:session 必须是同一个对象,不能在回调外新建或复用其他 session 实例。
事务提交后立即查,却没加 readConcern 或用了错误级别
MongoDB 默认 readConcern: "local",事务提交后,其他客户端(包括你自己的后续查询)可能因复制延迟或读关注设置,暂时看不到最新值。尤其在副本集上,主节点刚提交,从节点还没同步完。
立即学习“PHP免费学习笔记(深入)”;
- 想确保读到已提交结果,必须显式设
readConcern: "majority" - 但要注意:
"majority"要求多数节点确认,会增加延迟;若集群没开启majoritywrite concern,该设置无效 - PHP 中写法:
$collection->find($filter, ['readConcern' => new ReadConcern('majority')])
查的是另一个集合,或字段根本没被事务修改
事务只保证自身写入的原子性和隔离性,对“未参与事务”的数据无影响。常见误判场景:
- 事务里改了
orders集合的status,但你去users集合查name—— 这俩完全无关 - 事务更新用了
$set,但查询条件写成{ status: { $ne: null } },而原字段本来就是null,没变过 - 用了
upsert: true但匹配条件没命中任何文档,实际插入了一条新 doc,你却按旧 id 去查
PHP 进程里混用了不同连接或数据库实例
如果你用的是 Laravel 或自定义封装,容易在事务中用了一个 $db 实例,提交后却用另一个未绑定 session 的 $db2 去查。驱动不会自动把 session “粘”在连接池上,每次操作都得手动透传。
- 检查是否所有
find/findOne调用都带['session' => $session] - 避免全局复用
Collection对象:事务内应从同一Database实例获取 collection,并始终传 session - 别在事务回调外调用
$collection->findOne()—— 此时$session已结束或不可用
真正难调试的点在于:PHP 驱动不报错,它只是安静地执行了一个非事务查询。你得盯住每一条 find 调用的参数,而不是只看事务块有没有 commit 成功。



















