mongo权威指南(第三版)学习笔记

常用接口

  • insertone/insertMany
  • updateOne/updateMany/upsert/replaceOne
  • deleteOne/deleteMany

查询

MongoDB查询匹配的本质确实是:查询条件作用于"整个文档"的层面,只要"整个被查询的文档"满足查询条件中的所有要求,它就会被返回。

复合索引创建原则

1、第一个键优先使用等值字段

2、第二字段使用排序字段

3、第三个字段使用多值字段

疑惑点:为何这样的顺序设计复合索引性能往往较好,带着这个问题,尝试从他的原理去分析

原理:

只有在尽量小的范围内查询结果,那么查询的性能才能更好。所以复合索引创建原则就是为了实现的这个目标,总结的一般性原则。

我将用一个例子来说明我对于这个原则的理解:

假设有一个用户集合(users),包含以下字段:

city:用户所在城市(字符串类型,等值过滤字段)。

age:用户年龄(数值类型,范围过滤字段)。

username:用户名(字符串类型,排序字段)。

查询需求:查找城市为"New York"、年龄大于20岁的用户,并按用户名升序排序。

假设数据库中有New York、HongKong、Beijing三个城市,每个城市都有100000个记录

javascript 复制代码
db.users.find({ city: "New York", age: { $gt: 20 } }).sort({ username: 1 })

如果按照原则设计索引,那么如下创建

javascript 复制代码
db.users.createIndex({city:1, username:1, age:1})

那么查询过程如下:

  • city是等值过滤字段,精准匹配New York相关记录,就不需要遍历其余200000个其他城市的记录,一下子缩小了三分之二的查询数量。
  • 由于创建的索引是按照升序构造的,而我们的查询也是根据用户名进行升序排序,那么避免mongo进行内存排序
  • 由于 age是索引最后一个字段,过滤仅需检查每个匹配项的 age值,而不会破坏索引的顺序性。

这个过程的优势:

  • 减少键扫描量:等值字段 city将扫描范围缩小到城市为"New York"的子集,避免全索引扫描。

  • 消除排序成本:索引内排序避免内存排序(SORT阶段),降低CPU和内存开销。

  • 高效范围过滤:范围查询在匹配子集内进行,不会导致索引跳跃。

错误设计

创建索引:{ age: 1, city: 1, username: 1 }

同样是进行:查找城市为"New York"、年龄大于20岁的用户,并按用户名升序排序。

查询过程:

  • 索引首先按 age排序,但 age: {$gt: 20}是范围条件,因此MongoDB需扫描所有年龄大于20的索引项(从21到无穷大),无法快速定位匹配项。那么会产生很多无效的查询,例如city=Beijing和city=HongKong的文档都会被扫描
  • 索引按city排序,但查询需按 username排序,因此MongoDB无法直接使用索引顺序。它必须在内存中对结果进行排序(SORT阶段)。

该设计带来的代价:

  • 如果结果集大,可能触发32MB内存限制,导致错误或性能下降。
  • 范围查询在索引前端进行,导致扫描键数量远高于返回文档数(如扫描10万键返回100文档)。
  • 内存排序复杂度为O(n log n),而索引排序为O(n),数据量越大性能差异越显著。

总结

  • 等值字段优先:尽快缩小扫描范围,提升索引选择性。例如,city等值过滤直接定位城市子集。

  • 排序字段次之:利用索引有序性避免内存排序。例如,username在索引中维护顺序。

  • 范围字段最后:防止范围查询中断索引连续性。例如,age范围过滤在匹配子集内进行。

  • 通用性:该原则适用于大多数多条件查询,尤其是结合过滤和排序的场景。

  • 实践过程中,建议使用explain验证查询语句执行情况

索引桶

概念:索引桶是MongoDB中索引存储的基本单位。在MongoDB的底层存储引擎中,索引被组织为多个固定大小的存储单元,这些单元就是索引桶。每个索引桶包含一定数量的索引条目,这些条目按照索引键的顺序排列。

特点:

  • 固定大小:每个索引桶有固定的大小限制。(通常为4KB或16KB,具体取决于存储引擎配置)

  • 顺序存储:索引桶内的条目按照索引键的顺序排列,便于范围查询。

  • B树结构:索引桶通过B树(或B+树)结构组织,支持高效的查找、插入和删除操作。

工作原理

索引条目的存储方式

  • 每个索引桶包含多个索引条目,每个条目由以下部分组成:

  • 索引键值:文档中对应字段的值

  • 文档指针:指向实际文档位置的引用(记录ID)

  • 元数据:如时间戳、版本信息等

索引桶的分裂与合并

  • 当索引桶已满时,MongoDB会执行桶分裂操作:

  • 桶分裂:将过满的索引桶分成两个新桶,重新分配索引条目

  • 平衡操作:通过B树的自平衡特性维持查询效率

  • 空间回收:删除大量数据后,相邻的空桶可能会合并

索引遍历过程

查询时,MongoDB按以下步骤使用索引桶:

  • 从根节点开始,沿B树结构向下遍历

  • 在适当的索引桶中二分查找目标键值

  • 找到匹配的索引条目后,通过文档指针定位实际文档

限制和约束

大小限制

  • 条目大小限制:在MongoDB 4.2之前,索引条目必须小于1024字节,4.2之后取消了这个限制,如果一个文档的字段由于大小限制不能被索引,那么 MongoDB 就不会返回任何类型的错误或警告。这意味着大小超过 8KB 的键不会受到唯一索引的约束:比如,你可以插入多个相同的 8KB 字符串。

  • 总索引大小:单个集合的索引总大小不能超过RAM限制

  • 桶数量限制:过多桶会导致B树层级过深,影响查询性能

特殊键类型的限制

  • 数组字段:每个数组元素生成独立的索引条目,可能造成桶分裂

  • 大字符串:超过索引桶大小限制的键值无法被索引

  • 地理空间数据:使用特殊的GeoHash编码,占用更多桶空间

索引桶与唯一约束

创建唯一索引时,MongoDB需要确保:

  • 每个索引键值在桶中唯一

  • 插入重复键时会检查所有相关桶

  • 唯一索引的桶分裂操作需要额外的一致性检查

索引类型

唯一索引

地理空间索引​

用途:支持基于地理位置的高效查询(如邻近点查询、地理范围搜索)。

类型:

2dsphere:处理球面几何(地球表面)的查询。

2d:适用于平面坐标(如游戏地图)。

复合地理空间索引:可与其他字段(如时间戳)组合以优化复杂查询。

全文搜索索引​

功能:支持文本内容的语义搜索(如关键词匹配、相关性排序)。

多语言支持:通过指定语言选项(如default_language)支持不同语言的词干分析。

固定集合(Capped Collections)​

特点:

固定大小,写入新数据时会覆盖最旧的数据(循环写入)。

支持高性能写入和顺序读取(如日志存储)。

可追加游标(Tailable Cursors):实时监控新插入的数据,类似tail -f命令。

TTL索引(Time-To-Live)​

用途:自动删除过期数据(如会话信息、临时缓存)。

机制:基于时间字段(如createdAt)和expireAfterSeconds参数设置生命周期。

GridFS文件存储​

设计原理:将大文件分割为多个块(chunk),存储为独立文档。

结构:

fs.files:存储文件元数据(如文件名、大小)。

fs.chunks:存储二进制数据块。

相关推荐
技术小泽2 小时前
java转go速成入门笔记篇(一)
java·笔记·golang
诺狞猫2 小时前
黄山派播放TF卡MP4视频
学习·音视频·思澈·sifli
Noushiki2 小时前
RabbitMQ 进阶 学习笔记2
笔记·学习·rabbitmq
代码游侠2 小时前
复习——SQLite3 数据库
linux·服务器·数据库·笔记·网络协议·sqlite
Linux猿2 小时前
2025年中国AI学习平板市场洞察白皮书 | 附PDF
人工智能·学习·电脑·研报精选
你要飞2 小时前
Part 1 行列式
笔记·线性代数·考研
赵长辉4 小时前
AGI-rag学习:ChromaDB使用1,txt类型文档【20251016课复习】
python·学习·llm·agi
赵长辉4 小时前
AGI-rag学习:ChromaDB使用2,json类型文档,问答类内容,【20251016课复习】
学习·agi
じ☆冷颜〃7 小时前
分布式系统中网络技术的演进与异构融合架构(HFNA)
笔记·python·物联网·设计模式·架构·云计算