PHP事务需驱动≥1.7.0且服务端为4.0+副本集或4.2+分片集群;必须显式调用startTransaction()并绑定session,跨分片还需指定readConcern: "snapshot";事务内禁用mongoimport及隐式建集合。

PHP驱动必须 ≥ 1.7.0,且 MongoDB 服务端 ≥ 4.0 副本集
PHP 的 mongodb 扩展在 1.7.0 版本才正式支持事务 API(此前版本调用 startTransaction() 会直接报错 Method not found)。同时,服务端必须是 4.0+ 副本集或 4.2+ 分片集群——单节点 mongod 启动方式(如 mongod --port 27017)不支持事务,连接后执行 startTransaction() 会抛出 Transaction numbers are only allowed on a replica set member or mongos。
实操建议:
- 检查扩展版本:
php -r "echo phpversion('mongodb');",低于1.7.0必须升级:pecl install mongodb - 确认副本集就绪:连接后运行
$manager->executeCommand('admin', new \MongoDB\Driver\Command(['replSetGetStatus' => 1])),返回中members至少含一个"stateStr": "PRIMARY" - Docker 用户注意:启动容器时必须加
--replSet rs0参数,并在首次连接后手动执行rs.initiate()
事务必须显式绑定 session,不能漏掉 startTransaction()
PHP 驱动不会自动开启事务上下文。所有操作都需基于 MongoDB\Driver\Session 对象,且必须先调用 startTransaction() —— 否则看似“在事务里”,实则是普通写入,失败也不会回滚。
常见错误现象:
立即学习“PHP免费学习笔记(深入)”;
- 只调用
$session = $manager->startSession(),但没调$session->startTransaction()→ 插入/更新立即生效 - 调了
startTransaction()却忘了commitTransaction()或abortTransaction()→ 事务挂起,60 秒后超时自动中止,占用会话资源 - 在事务中执行
find()未指定readConcern→ 报错Read concern 'snapshot' is not available for this operation
正确写法示例(带错误捕获):
$session = $manager->startSession();
try {
$session->startTransaction([
'readConcern' => ['level' => 'snapshot'],
'writeConcern' => ['w' => 'majority']
]);
$bulk = new \MongoDB\Driver\BulkWrite();
$bulk->insert(['status' => 'pending', 'ts' => new \MongoDB\BSON\UTCDateTime()]);
$manager->executeBulkWrite('test.orders', $bulk, ['session' => $session]);
$bulk = new \MongoDB\Driver\BulkWrite();
$bulk->update(['sku' => 'abc'], ['$inc' => ['qty' => -1]]);
$manager->executeBulkWrite('test.inventory', $bulk, ['session' => $session]);
$session->commitTransaction();
} catch (\MongoDB\Driver\Exception\RuntimeException $e) {
$session->abortTransaction();
throw $e;
} finally {
$session->endSession();
}
跨分片事务要额外传 readConcern: "snapshot" 到 startTransaction()
在分片集群上,事务默认不启用 snapshot 隔离,必须显式在 startTransaction() 的选项里声明,否则哪怕只写不读,某些驱动版本(如 PHP 1.12+)也会因内部元数据查询失败而报错。
关键点:
- 不能在单个操作里加
readConcern(如['readConcern' => ['level' => 'snapshot']]),必须传给startTransaction() - 每个 shard 必须是三节点副本集,
config server也必须是副本集,mongos版本 ≥ 4.2 - 运行
sh.status()确认所有 shardstate为1,且shardingVersion≥1
错误示范:$session->startTransaction() → 缺选项,跨分片事务必然失败;正确写法:$session->startTransaction(['readConcern' => ['level' => 'snapshot']])。
事务内不能用 mongoimport,也不能隐式建集合
PHP 中的事务只包裹通过 $manager->executeCommand() 或 $manager->executeBulkWrite() 发起的、显式传入 ['session' => $session] 的操作。任何外部工具或 DDL 操作都不受控。
典型陷阱:
- 在事务块里调用
shell_exec('mongoimport ...')→ 子进程无会话上下文,写入完全脱离事务 - 用
insertMany()写入时目标集合不存在 → 事务内不允许隐式建集合,报错NamespaceNotFound - 试图在事务中执行
createCollection命令 → 4.4+ 虽支持create选项,但 PHP 驱动尚未暴露该能力,仍会失败
安全做法:提前建好所有涉及的集合(db.createCollection('orders')),导入逻辑改用 PHP 解析 JSON 后分批调用 executeBulkWrite(),每批控制在 100–500 条以内,避免触发 TransactionTooLargeForCache 或 60 秒超时。
session 参数的那些操作。任何绕过驱动、未显式绑定会话、或触发隐式 DDL 的行为,都会让“原子性”彻底失效。最容易被忽略的是副本集初始化和 readConcern 选项——这两个点没到位,代码写得再完整也没用。



















