mongodb事务仅支持4.0+副本集或4.2+分片集群;本地单节点需启动时加--replset rs0并执行rs.initiate();pymongo中必须显式调用session.start_transaction(),且读操作需指定read_concern="snapshot"。

事务报错 Transaction numbers are only allowed on a replica set member or mongos 怎么办
这说明你正在单节点 mongod 上尝试开启事务,MongoDB 明确不支持。事务只在 4.0+ 副本集(replica set)或 4.2+ 分片集群中可用。
本地开发最简修复方式:
- 停掉当前 mongod 进程
- 用
--replSet rs0启动:mongod --replSet rs0 --port 27017 --dbpath /data/db - 连接后执行
rs.initiate(),再运行rs.status()确认状态为PRIMARY - Docker 用户需在
docker run中加--replSet rs0,且容器启动后必须进 shell 手动调用rs.initiate()
云服务(如 Atlas)默认是副本集,跳过此步;但本地直接 mongod 启动必报这个错,没商量。
PyMongo 中 session.start_transaction() 必须显式调用
PyMongo 不会自动把操作塞进事务——哪怕你传了 session=... 参数,没调 start_transaction() 就等于裸写入。
常见错误现象:
-
collection.insert_one({...}, session=session)却没先调session.start_transaction()→ 数据立刻落盘,异常时不会回滚 - 调了
start_transaction()但忘了commit_transaction()或abort_transaction()→ 事务卡住,60 秒后自动 abort,日志里可能只看到超时警告 - 在
with session.start_transaction():外部执行带session的操作 → 报错Transaction is not in progress
正确姿势是用上下文管理器嵌套,或手动 try/except 控制生命周期:
with client.start_session() as session:
with session.start_transaction():
db.orders.insert_one({"status": "pending"}, session=session)
db.inventory.update_one({"sku": "abc"}, {"$inc": {"qty": -1}}, session=session)
事务内集合和读关注有硬性限制
不是所有集合都能进事务,也不是所有查询都能在事务里跑。
必须满足:
- 集合不能是
capped: true类型(比如日志集合常设 capped,一设就废) - 不能是
system.*集合(如system.users) - 如果集合创建时指定了非
simple的 collation,事务中所有读操作必须显式加read_concern="snapshot" - 事务内所有读操作默认要求
read_concern="snapshot",否则可能报错Read concern 'snapshot' is not available for this operation
所以建议:事务涉及的集合统一用默认 collation 创建,并在事务块内所有 find()、count_documents() 等读操作都带上 session=session, read_concern=ReadConcern("snapshot")。
session 对象不能跨 client 复用
ClientSession 和创建它的 MongoClient 实例强绑定。拿 A client 开的 session 去 B client 的 collection 上操作,会直接报错或行为未定义。
容易被忽略的点:
- 不要在多个线程间共享一个 session(session 不是线程安全的)
- 不要把 session 存成模块级变量或全局单例(并发下会串话)
- 每个事务逻辑应独占一个 session,用完即弃(
end_session()会自动触发) - 频繁开启事务时,复用同一个
MongoClient实例,但每次事务都调client.start_session()新建 session
事务本身不难写,难的是部署环境配对、session 生命周期卡点、集合类型合规这三处——漏一个,事务就静默失效或直接崩掉。











