MongoDB笔记
最近在整理mongodb相关的知识,正好写一篇文章。主要介绍mongodb的结构,索引相关的理论。
MongoDB架构
+---------------------+
| 客户端请求入口 | ← 客户端连接(TCP/IP)
+---------------------+
↓
+---------------------+
| 网络接口层 | ← 处理连接池、协议解析(如BSON)
+---------------------+
↓
+---------------------+
| 查询处理器 | ← 解析命令(CRUD、聚合等)
| - 查询优化器 | ← 生成执行计划,选择索引
| - 聚合管道引擎 | ← 处理$match、$group等阶段
+---------------------+
↓
+---------------------+
| 存储引擎接口层 | ← 抽象层(支持WiredTiger、In-Memory等引擎)
+---------------------+
↓
+---------------------+
| 存储引擎(WiredTiger) | ← 实际数据管理
| - 缓存管理(Cache) | ← 数据内存缓存(LRU策略)
| - 事务控制 | ← ACID事务支持
| - 压缩与加密 | ← 数据压缩(Snappy/Zlib)和加密
| - 日志系统(Journal) | ← 预写日志(WAL)确保崩溃恢复
+---------------------+
↓
+---------------------+
| 数据文件系统层 | ← 持久化存储(.wt文件)
+---------------------+
应用通过提供的mongodb-driver客户端连接到mongodb,传输bson数据。mongodb接收到请求后,解析查询语句,分析索引,调用存储引擎查询数据。
查询过程如下:
-
将查询条件bson构建为语法树(query_tree),然后将操作符展开,如填充$eq , 将and条件展开等等
// 原始查询 { age: { $gt: 18 }, status: "active" } // 内部解析为 { $and: [ { age: { $gt: 18 } }, { status: { $eq: "active" } } ] } -
查询预估与优化,然后索引选择。
db.collection.find({ a: 5, b: { $lt: 10 } }).explain("executionStats") 通过 queryPlanner 分析不同索引的执行成本(如扫描文档数、索引键大小) -
存储引擎处理。
-
内存筛选,如果无法直接通过索引判断数据是否符合,则需要加载到内存中进一步判断。
// 索引无法直接处理正则表达式(除非左锚定) { name: { $regex: /^Al/ } } // 可以使用索引 { name: { $regex: /son$/ } } // 无法使用索引,触发全集合扫描 -
游标生成与projection,返回匹配的文档的游标,可以通过batchSize分批获取。如果设置了project,会在这一步剔除字段。
关键特性
-
缓存配置:
# mongod.conf storage: wiredTiger: engineConfig: cacheSizeGB: 2 # 默认占用内存的50% -
监控指标:
- db.serverStatus().wiredTiger.cache 查看缓存命中率。
- 监控锁竞争: db.currentOp({ "locks": { $exists: true } })
- 压缩效率: db.collection.stats().wiredTiger["block-manager"]["bytes compressed"]
存储引擎
默认为WiredTiger。
-
核心特性:
- 文档级并发控制:通过 MVCC(多版本并发控制)实现读写操作的并发执行。
- 压缩算法:支持 Snappy(默认)和 Zlib 压缩,减少磁盘占用。
- 事务支持:提供多文档 ACID 事务(4.0+ 版本)。
- 内存缓存:使用 LRU 缓存管理热数据,通过 wiredTigerCacheSizeGB 配置。
- 索引结构:基于 B-Tree 和 LSM Tree(针对写入优化场景)。
-
适用场景:生产环境通用型引擎,适合高并发读写和事务需求。
-
配置示例
# mongod.conf storage: engine: wiredTiger wiredTiger: engineConfig: cacheSizeGB: 8 journalCompressor: snappy collectionConfig: blockCompressor: zlib # 集合数据压缩算法 indexConfig: prefixCompression: true # 索引前缀压缩
MongoDB使用命令
具体各个操作命令的使用可以看这篇文章。
golang操作mongodb:juejin.cn/post/690806…
操作符
操作符是mongodb中处理数据的行为,类似于一个个方法,不同的操作符作用与不同的阶段,可以发挥不同的作用。
- 查询操作符:www.mongodb.com/zh-cn/docs/…
- 更新操作符:www.mongodb.com/zh-cn/docs/…
- 聚合操作符:www.mongodb.com/zh-cn/docs/…
-
维度 查询操作符 更新操作符 聚合操作符 执行阶段 数据读取阶段 数据写入阶段 数据处理阶段 作用对象 文档集合 单文档或批量文档 文档流(Pipeline) 核心目标 筛选目标文档 修改文档内容或结构 数据转换与计算 原子性 无 单文档级别原子性 无(管道整体可事务化) 性能影响 依赖索引优化 写锁竞争优化 内存与管道阶段优化 典型场景 条件查询 字段级增量更新 OLAP 分析、数据清洗
MongoDB索引
mongodb的集合可以设置索引,加快数据查找。索引的使用与关系型数据库的索引知识一样,遵循的范式也类似。
| 操作符类型 | 索引支持情况 | 优化建议 |
|---|---|---|
| 等式操作符 | in 可高效使用索引 | 优先作为复合索引的前缀字段 |
| 范围操作符 | lt, lte 可使用索引,但复合索引中需放在等式字段之后 | 避免范围查询字段在复合索引的前列 |
| 逻辑操作符 | or 可能导致多个索引扫描 | 对 $or 子句分别建索引 |
| 数组操作符 | size 无法使用索引 | 对数组字段建多键索引,避免 $size 全扫描 |
| 正则表达式 | 左锚定正则(如 /^prefix/)可使用索引,其他情况触发全扫描 | 优先使用左锚定正则表达式 |
| 地理空间操作符 | 必须依赖 2dsphere 或 2d 索引 | 确保地理字段有对应索引 |
MongoDB 默认使用 B树(B-Tree) 作为索引的底层存储结构,而非 B+树.(这是官网的说法,也有人说是B+树)。
为什么使用B树?由于关系型数据库和非关系型数据的设计方式上的不同。在关系型数据中,遍历操作比较常见,因此采用B+树作为索引比较合适。而在非关系型数据库中,单一查询比较常见,因此采用B树作为索引比较合适。B树是平衡树并且有序,树的节点存有数据key与对应data指针,可以快速查找到具体的值,适合非关系型数据库面向点对点的查找场景。
关系型数据库会使用B+树而不是B树,因为B+树在B树的基础上,只有叶子节点存数据并将叶子节点通过链表串起来,这种结构设计适合关系型数据库对范围查询的场景。
索引使用
db.collection.createIndex({ field: 1 }, { options })
db.collection.dropIndex("index_name")
db.collection.reIndex({ name: "index_name" }) 注意事项:重建期间会阻塞写入操作,建议在低峰期执行
查看集合所有索引
db.collection.getIndexes()
db.collection.find(query).explain("executionStats")
- queryPlanner(默认):展示索引选择逻辑。
- executionStats:统计实际执行耗时和扫描文档数。
- allPlansExecution:对比不同候选索引的执行情况。
索引优化
-
数组元素的快速查找:$elemetMatch (可以触发索引扫描,但是仍然需要二次过滤)
// 正确用法 db.test.find({ scores: { $elemMatch: { $gt: 10, $lt: 20 } } }) // 错误用法(会匹配不同元素分别满足条件的文档) db.test.find({ scores: { $gt: 10, $lt: 20 } }) -
高效覆盖索引
// 低效写法(可能触发全表扫描) db.users.find({ age: { $ne: null } }) // 高效替代(明确要求字段存在) db.users.find({ age: { $exists: true, $ne: null } }) -
分页查询使用_id区分,避免skip的性能问题
// 第一页 const firstPage = db.logs.find().sort({ _id: 1 }).limit(100); const lastId = firstPage[99]._id; // 下一页 const nextPage = db.logs.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(100);
MongoDB事务
Mongodb支持事务操作,在批处理数据的时候能够形成原子操作。并且,在mongodb集群中,也支持分布式事务。
ACID 支持:
- 原子性(Atomicity):事务内的操作要么全部成功,要么全部回滚。
- 一致性(Consistency):事务保证数据从一种有效状态转换到另一种有效状态。
- 隔离性(Isolation):默认隔离级别为 快照隔离(Snapshot Isolation),避免脏读和不可重复读。
- 持久性(Durability):提交后的事务数据持久化到磁盘。
事务流程
-
创建session
-
设置读写关注(read concern , write concern)
配置 一致性 可用性 适用场景 readConcern: "local" 低 高 实时性要求高,容忍脏读 writeConcern: { w: 1 } 低 高 快速写入,允许数据短暂丢失 readConcern: "majority" 高 中 强一致性场景 -
提交或者回滚
事务使用
事务监控
查看活跃事务
db.currentOp({ "lsid": { $exists: true } })
事务统计
db.serverStatus().transactions
MongoDB集群模式
mongodb以扩展能力强著称,可以配置副本集(replica)集群模式支持高可用,也可以使用分片集群模式(sharding)支持海量数据的分片存储。
-
Replica
- 由一主(Primary)、多从(Secondary)和可选仲裁节点(Arbiter)组成,节点数建议为奇数以支持自动选举。
- 各个节点之间数据一致。
- 主节点异常时,副本集可以通过选举,得到新的主节点,实现高可用。
-
Sharding
- 由分片服务器(Shard)、配置服务器(Config Server)和路由服务器(mongos)组成。
- shard存储实际数据,每个分片通常是一个副本集,避免单点故障。
- config server 存储集群元数据(如分片键范围),需部署为副本集以确保元数据安全
- mogons 无状态组件,负责将客户端请求路由到对应分片,并聚合结果
-
客户端 → mongos路由层 ↓ 配置服务器(副本集,存储元数据) ↓ 分片1(副本集:Primary + Secondary + Arbiter) 分片2(副本集:Primary + Secondary + Arbiter) ...其他分片
-
集群模式下的核心机制
- 通过副本集之间的选举兜底机制,实现高可用
- oplog, 主节点记录所有写操作,从节点异步拉取并重放日志,保证最终一致性
- 读写关注(Read/Write Concern):可配置数据写入的确认级别(如“多数节点确认”),平衡性能与一致性
参考
www.geeksforgeeks.org/mongodb-arc…
附录
B树与B+树
B树
[根节点]
/ | \
10 30 50
/ / \ \
[叶] [叶][叶] [叶]
- 每个节点存储键值(Key)与数据指针(Data Pointer)
- 叶子节点和非叶子节点均可能包含数据
- 所有叶子节点位于同一层级(平衡树)
B+树
[根节点]
/ | \
10 30 50
/ / \ \
[叶]→[叶]→[叶]→[叶]→...
- 非叶子节点仅存储键值(不存数据指针)
- 所有数据存储在叶子节点,并通过指针串联成链表
- 叶子节点包含全量键值副本