架构设计复制集架构在谈具体的读写前必须先理解复制集Replica Set。它是 MongoDB 保证数据安全和自动容灾的基础。Primary主节点接收所有的写操作并将变更记录到 oplog操作日志中。Secondary从节点定期从主节点异步复制 oplog 并应用到自身保持数据同步。默认情况下从节点不处理读写但可以配置为只读。Arbiter仲裁节点可选不存储数据不参与读写纯粹为了凑足选举票数。心跳机制节点间每 2 秒发送一次心跳。如果 Primary 挂掉Secondary 节点们会在毫秒级内自动发起选举选出新的 Primary实现自动故障转移。分片集群架构MongoDB 最核心的优势在于支持分片集群Sharded Cluster实现海量数据的水平扩展。一个完整的 MongoDB 集群架构通常由以下三个核心组件构成Mongos路由服务器客户端的统一入口。功能它是个无状态的代理不存储数据。当收到客户端的读写请求时它会去 Config Server 查询数据路由表然后将请求精准转发到对应的分片Shard上Config Server配置服务器集群的“大脑”与元数据存储。功能存储整个集群的元数据和路由信息比如哪个范围的数据在哪个分片上。为了保证高可用生产环境中 Config Server 必须部署为一个复制集Replica SetShard分片/数据节点真正存储数据的容器。功能每个分片负责存储总数据的一部分。为了防止单点故障每个 Shard 在生产环境中都是一个独立的复制集Replica Set写流程MongoDB 的写操作Insert/Update/Delete默认必须在 Primary 节点上执行。现代 MongoDB 默认使用 WiredTiger 存储引擎。节点的内部写步骤以 Primary 为例当一个写请求到达 Primary 节点时WiredTiger 引擎在内存和磁盘中的交互流程如下内存更新数据首先写入 WiredTiger 的内存缓冲区Cache并在内存中修改 B 树B-Tree索引。写 Journal 日志预写日志内存修改完成后WiredTiger 会将这次修改的具体操作类似于变动记录写入到内存中的 Journal Buffer日志缓冲区根据设置默认每 100ms或者满足 128KB 触发Journal Buffer 中的数据会被执行系统的 fsync 刷入磁盘的 journal 文件中类似于 MySQL 的 Redo Log写 Oplog同时该操作会被记录到 local.oplog.rs 集合中用于后续的节点间复制。返回客户端根据客户端配置的 Write Concern写安全级别决定何时向客户端返回成功信号。Data Checkpoint异步刷盘内存中的脏数据并不会立刻写入真正的数据文件。WiredTiger 默认每 60 秒或脏数据达到一定比例做一次 Checkpoint将内存中的修改真正同步到磁盘的数据文件中。这种“先内存 顺序写日志”的设计将随机的磁盘 IO 变成了顺序的日志 IO是 MongoDB 写入性能极高的核心原因。什么是 Write Concern写关注写流程返回成功的时机由 w 参数决定w: 1默认Primary 节点将数据写入内存和 Journal 成功后立刻返回成功。w: “majority”数据必须被写入大多数超过半数可见节点后才向客户端返回成功。这是防止数据在极端异常下被回滚的最安全做法。WAL预写日志的真实含义“预写”指的是日志Journal落盘的时机要早于数据Data落盘的时机。只要 Step 3日志落盘 完成了Step 5数据落盘 即使没做也无所谓。宕机后MongoDB 会读取磁盘上的 Journal 日志把没来得及刷盘的数据在内存中“重做”一遍这就保证了持久性Durability读流程读策略Read Preference客户端可以通过设置 Read Preference 来决定将读请求发送给谁primary默认只从主节点读。保证强一致性能读到最新的写。primaryPreferred优先从主节点读挂了再读从节点。secondary只从从节点读读写分离适合报表等非实时高并发查询。secondaryPreferred优先从从节点读。nearest根据网络延时优先选择最近的节点读。单节点内部的读步骤假设读请求落到了某个节点上检查 CacheWiredTiger 引擎首先看请求的数据页Page是否在内存 Cache 中。磁盘加载如果命中直接从内存返回如果未命中产生 Page Fault缺页中断从磁盘数据文件中将数据页加载到 Cache 中。索引加速如果有索引会通过 B-Tree 索引快速定位数据页如果是全表扫描COLLSCAN则需要加载大量数据页。读数据效率高的原因在 MongoDB 中当你创建任何一个集合时系统都会自动为 _id 字段创建一个唯一的主键索引B树 的“高度”从根节点到叶子节点的层数增长非常缓慢。对于上亿条数据B树 的高度通常只有 3到5层。这意味着即使在磁盘上找也只需极少量的磁盘 I/O用 _id 查询速度极快根本原因在于 _id 索引几乎必然会完整地常驻内存理由非常充分战略优先级WiredTiger 官方明确指出在内存压力下引擎会 “优先确保索引缓存” 。这意味着索引比普通文档数据更难被“踢出”内存物理尺寸小_id 索引是 MongoDB 中“最瘦”的索引。一个 12 字节的 ObjectId 比动辄几百字节的普通文档小得多即使在上亿数据量下其整体大小也更容易被内存容纳天生的“热数据”作为主键_id 是最频繁的查询入口。根据“最近最少使用”算法被高频访问的 _id 索引页会一直处于“活跃”状态永远不会被系统视为“冷数据”而淘汰