我从MongoDB 2.6开始用,当时连$lookup都没有,聚合框架还像个玩具。后来4.0引入复制集事务,我激动地准备把用户系统迁移过去,但压测结果让我冷静下来。在默认writeConcern下,事务的吞吐比单文档操作低了近6倍,而一旦你为了数据安全加上writeConcern: majority,延迟直接突破200ms。这不是MongoDB不行,而是分布式事务本身就包含了巨大的协调成本。问题在于,很多开发者只看到事务的“承诺”,忽略了它背后的网络RTT、锁竞争和快照隔离的代价。
你以为的ACID和实际的ACID,差距比想象中大得多。传统关系型数据库的ACID基于单机共享存储,而MongoDB的ACID是建立在分布式副本集之上的。它用readConcern: snapshot实现快照隔离,用writeConcern: majority保证跨节点持久化,但这两者组合起来,在大部分读写分片场景下会导致事务在多个分片间协调,而MongoDB的分布式事务是基于两阶段提交,协调者(mongos)和参与者的日志都要写盘,性能崩溃是必然的。我自己测试过,在3分片、每个分片3副本的集群上,一个只更新两条跨分片文档的事务,TPS不超过500,而同样的逻辑用Redis加队列的最终一致性方案,TPS可以到2万。这个对比极其残酷:MongoDB事务适合低并发、强一致性的金融级操作,但绝对不适合互联网业务中那些“看起来需要事务”的地方。
版本升级真的解决了吗?MongoDB 4.2支持分片事务,4.4优化了重试逻辑,5.0引入了时间戳和快照的改进,6.0的聚合阶段几乎都能在事务里用,7.0把事务内操作也扩展了。但最核心的问题——事务的隔离级别和锁粒度——始终没有实质改善。MongoDB的事务默认采用乐观并发控制,但内部还是基于修改时版本号冲突检测,一旦你的事务写入同一个文档,就会产生大量WriteConflict错误。官方文档建议用retryWrites,但重试次数默认只有100次,而每次重试都会浪费整个事务的执行时间。我在生产环境里把事务超时从60秒调成10秒,反而成功率提高了,因为长事务只会放大冲突概率。
我的真实建议是:不要因为MongoDB宣传“支持事务”就把它当成关系型数据库。如果你需要严格的事务和ACID,用PostgreSQL或者Oracle,它们经过了三十年锤炼。如果你愿意在一致性上妥协,用最终一致性的方案,比如Kafka加状态机,比MongoDB事务快一个数量级。MongoDB事务的适用场景其实很窄:一是你被MongoDB的数据模型锁死,二是对写延迟不敏感的后台任务,三是事务内没有热点的低频操作。我现在会把需要事务的数据单独放进PostgreSQL,而MongoDB只负责用户行为日志和商品资料。这样的混合架构,让我的系统既有了一致性保证了,又能享受MongoDB的灵活文档模型。别把MVP和核心账务混在一起,那才是真正的灾难。