Skip to content

MongoDB:模型、索引、原子性与一致性 ​

前置是文档、集合、BSON 和过滤器。适用 MongoDB 8.0 版本族、PyMongo 4.x,核对 2026-10-17;下面的查询和事务均未执行。

内嵌与引用的取舍 ​

MongoDB 内嵌模型把经常一起读写、有界增长的数据放进同一文档,引用模型把独立生命周期或高增长数据拆开。任务状态和版本适合放一起,无限评论列表通常要拆集合。灵活模式不是没有模式:应用需检查输入,集合验证器可限制字段类型和状态范围,已有坏数据还需迁移策略。官方数据建模

模型先问访问模式:需要按谁筛选、读多少项、如何更新以及规模如何增长。多表 JOIN 思维直接平移可能造成大量往返;反过来,把所有关联都内嵌也会造成大文档和写热点。无界增长的数组应考虑拆成独立集合并引用父文档标识,让分页、删除和容量控制有明确边界。本例只存任务当前状态,没有假装包含完整事件审计。

联合索引与 explain ​

MongoDB 联合索引 owner,status,_id 主要服务以 owner 开头的过滤与对应排序。索引前缀影响可用方式,排序方向和等值/范围条件也影响是否需要额外排序。索引不是字段排列的任意集合,不能凭“都在索引里”就保证最优。explain 的 winningPlan、totalKeysExamined、totalDocsExamined 用来检查扫描成本;小样本计划不能代表生产。官方联合索引

数组字段产生 multikey 索引,复合数组索引有额外限制;唯一索引还涉及缺失值、null 以及分片键边界。先检查已有重复数据,再建立唯一约束;不要把普通 createIndex 当作自动去重。示例 list 是全体任务按 _id 排序,主要依赖 _id 索引;owner_status_id 是供练习按 owner、status 查任务使用,不冒称它优化所有查询。

聚合管道与基数变化 ​

MongoDB 聚合管道把数据依次交给 $match、$group、$sort、$project 等阶段。$group 按状态汇总任务数,结果顺序需要额外 $sort;$unwind 会把数组展开成多行,因此计数之前必须明确统计的是任务还是元素。尽早可下推的过滤常能减少后续输入,但优化器行为仍要用 explain 检查。官方聚合

$group: {_id:"$status",count:{$sum:1}} 按文档计数,字段缺失可能与预期状态域不一致。聚合不保证所有阶段都由索引完成,也不意味着计算结果自动持久化。练习:一个任务有三个标签,unwind 后 count 是几?解答:三条展开结果,若要任务数需先按任务去重或不要展开。

单文档原子更新与版本条件 ​

MongoDB 单文档原子性保证同一文档的一次更新不会对外暴露半套字段。把 _id、version、status 放进 find_one_and_update 过滤器,匹配时同时 $set 状态并 $inc 版本,就能检测过期提交;只按 _id 更新则可能让后写覆盖先写。未匹配并不自动说明数据库故障,它也可能是版本过期或任务已完成。官方原子性

updateMany 对每个文档的更新原子,不代表整批多文档一起原子。本示例每次操作只需一份任务文档,不需要多文档事务;这来自业务模型,不能推广成 MongoDB 永远无需事务。

事务拓扑与重试边界 ​

MongoDB 多文档事务要求副本集或分片集群,独立 standalone 不能作为等价事务环境。副本集复制同一数据集,分片则按分片键分布数据,两者解决的问题不同。使用会话和 with_transaction 时回调可能重试,回调里的邮件或支付等外部副作用必须另行安排幂等和提交后处理。官方事务

跨集合库存与订单若必须同时成立,可以在合适拓扑中使用事务并保持操作短小;不能用两次独立更新冒充原子提交。驱动的瞬态事务重试和提交结果不确定处理有不同目的,业务仍需一个可查询的请求身份。事务隔离、读关注和提交写关注要一起设计,而不是只加 start_transaction。

读写关注与因果一致性 ​

MongoDB majority 写关注表示按拓扑要求获得多数确认,majority 读关注选择多数提交的数据;readPreference 决定把读发到哪里,它与 readConcern 不是一回事。副本延迟意味着从 secondary 读取可能旧于刚完成的写入,不能把 majority 简化成所有节点立即同步。官方一致性

需要会话内因果顺序时,应按官方条件使用因果一致会话及 majority 读写关注。线性化读取又有独立适用条件和性能代价。本例独立 CLI 进程间没有共享因果会话,所以 list 仅提供当前查询,不承诺跨进程“读己之写”的一般证明。wtimeout 可能表示确认超时,不能据此断言写入一定没发生;重试前应查业务身份。

继续阅读