思维导图学《Mongo 官方文档》

本文是对 Mongo 官方文档粗略的总结,并没有涉及到很深的细节(细节还是直接看官方文档吧)。我认为 Mongo 有重要的就 3 点:

存储引擎原理,如何保证断电后恢复数据?Mongo 的 data 在文件系统中,是如何组织和保存的?

Replication

Sharding

思维导图

思维导图学《Mongo 官方文档》_第1张图片

目录

思维导图学《Mongo 官方文档》_第2张图片

Basic

思维导图学《Mongo 官方文档》_第3张图片

Aggregation & Data Modeling

思维导图学《Mongo 官方文档》_第4张图片

Indexes

思维导图学《Mongo 官方文档》_第5张图片

Storage

思维导图学《Mongo 官方文档》_第6张图片

Journal&GridFS

思维导图学《Mongo 官方文档》_第7张图片

Replication & Sharding

思考

Document 在内部是如何存储的?

每个 Document 被保存在一个Record中。Record 相当于 MongoDB 内部分配的一块空间,除了保存 Document 的内容可能还会预留一些填充的额外空间。对于写入后的 Document 如果还会更新,可能导致 Document 长度增加,就可以利用上额外的填充空间来。若业务对于写入后的 Document 不会再更新或删除(像监控日志、流水记录等),可以指定无填充的 Record 分配策略,更节省空间。

思维导图学《Mongo 官方文档》_第8张图片

单个 Document 的容量是否有限制?

16MB。Document 这种 JSON 形态天生会带来数据存储冗余,主要是 field 属性每个 Document 都会保存一遍。目前 3.2 版本的 MongoDB 已经将新的 WiredTiger 作为默认存储引擎,它提供了压缩功能,有两种压缩形式:

Snappy默认压缩算法,在压缩率和 CPU 开销之间取得平衡。

Zlib更高的压缩率,但也带来更高的 CPU 开销。

而每个 Document 依然有最大容量限制,不能无限增长下去,这个限制目前是 16MB。那么我要存大于 16MB 的文件怎么办,MongoDB 提供了 GridFS 来存储超过 16MB 大小的文件。如下图所示,一个大文件被拆分成小的 File Chunk,每个 Chunk 大小 255KB,并存放在一个 Document 中。GridFS 使用了 2 个 Collection 来分别存放文件 Chunk 和文件元数据。

思维导图学《Mongo 官方文档》_第9张图片

遇到真正的「大数据」(单机存储容量不够)怎么办?

分片化:利用更多的机器来提供更大的容量,分片集群采用代理模式:

思维导图学《Mongo 官方文档》_第10张图片

而每个分片上的数据又以Chunk的形式组织(类似于 Redis Cluster 的 Slot 概念),以便于集群内部的数据迁移和再平衡。比较容易混淆的是这里的 Chunk 不是前面 GridFS 里提到的 Chunk,它们的关系大概如下图:

思维导图学《Mongo 官方文档》_第11张图片

Mongo 的数据安全吗?在保证效率的同时,在服务器突然宕机的情况下,是否能够保存数据?

安全效率其实是相互制约的,越安全则效率越低,越高效则越不安全。MongoDB 的设计场景考虑的是应对大量的数据写入和查询,而数据的重要性相对没那么高。所以 MongoDB 的默认设置在安全和效率之间,更偏向效率。

思维导图学《Mongo 官方文档》_第12张图片

Write To Buffer Without ACK

这个模式下 MongoDB 是不确认写请求的,Client 端调用驱动写入后若没有网络错误就认为成功,实际到底写入成功没有是不确定的。即使网络没有问题,数据到达 MongoDB 后它先保存在内存 Buffer 中,再异步写入 Journaling 日志,这中间有 100ms(默认值) 的落盘(写入磁盘)时间窗口。一般数据库的设计都是先写 Journaling 的流水日志,随后异步再写真正的数据文件到磁盘,这个可能就比较长了,MongoDB 是 60 秒或者 Journaling 日志达到 2G。

思维导图学《Mongo 官方文档》_第13张图片

Write To Buffer With ACK

这个比上一种模式稍微好一点,MongoDB 收到写入请求,先写入内存 Buffer 后回发 Ack 确认。Client 端能确保 MongoDB 收到了写入数据,但依然有短暂的 Journaling 日志落盘时差导致潜在的数据丢失可能。

思维导图学《Mongo 官方文档》_第14张图片

Write To Journaling With ACK

这个模式确保至少写入 Journaling 日志后才回发 Ack 确认,Client 端能确保数据至少写入磁盘了,安全性较高。

思维导图学《Mongo 官方文档》_第15张图片

Write To Replica Buffer With ACK

这个模式是针对多副本集的,为了提升数据安全性,除了及时写入磁盘也可以通过写多个副本来提升。在这个模式下,数据至少写入 2 个副本的内存 Buffer 中才回发 Ack 确认。虽然都在内存 Buffer 中,但两个实例在落盘短暂的 100ms 时差中同时故障的概率很低,所以安全性有所提升。

MMAPv1 和 WiredTiger 有什么区别?

MMAPv1 是 Mongo 在 3.0 以前的存储引擎,WiredTiger 是 Mongo 在 3.2 及以后版本的默认存储引擎;

MMAPv1 只是单纯地将 BSON 数据直接存储在磁盘上,WiredTiger 则会在数据从内存存储到磁盘前进行一次压缩

MMAPv1 在 3.0 版本之前,以 database 为单位加锁,对同一个Database的其他Collection所做的操作也会被阻塞。 而到了 3.0 版本,MMAPv1 则开始使用以 Collection 为单位的加锁。WiredTiger 是基于Document 级锁机制。

MMAPv1 是如何分配记录的?

在MongoDB中,每条数据以Document的形式进行存储,并通过 Collection 来管理Document。同一个Collection中的Document会根据插入(insert)的先后顺序, 连续地写入到磁盘的同一个区域(region)上。MMAP在第一次插入时会为每个Document开辟一小块专属的区域,你可以管它叫一个"record"(记录),或一个"slot"(record这个名字容易和别的东西混淆,所以后面我会管它叫slot), 其他新插入的Document则必须从这一小块区域的结尾处开始写入。

为了避免 update 时 Document 变大重新分配空间,创建 Document 时会预留一定的空间,称为padding,可以降低重新分配 Document 的几率。

WiredTiger 是如何实现 Document 级锁的?

在平常的使用中,大多数对数据库的更新操作都只会对某个 Collection 中的少量 Document 进行更新。对多个Collection进行同时更新的情况已是十分稀有,对多个 Database 进行同时更新则是更为罕见了。 由此可见,加锁粒度最小只支持到 Collection 是远远不够的。相对于 MMAPv1,WiredTiger 使用的实际为 Document 级的乐观锁机制。

WiredTiger的乐观锁机制与其他乐观锁机制实现大同小异。WiredTiger会在更新Document前记录住即将被更新的所有Document的当前版本号,并在进行更新前再次验证其当前版本号。 若当前版本号没有发生改变,则说明该Document在该原子事件中没有被其他请求所更新,可以顺利进行写入,并修改版本号;但如果版本号发生改变,则说明该Document在更新发生之前已被其他请求所更新, 由此便触发了一次“写冲突”。不过,在遇到写冲突以后,WiredTiger也会自动重试更新操作。

你可能感兴趣的:(思维导图学《Mongo 官方文档》)