想要深入了解MongoDB如何存储数据之前,有一个概念必须清楚,那就是 Memeory-Mapped Files。

Memeory-Mapped Files
下图展示了数据库是如何跟底层系统打交道的。 (虚拟内存是计算机系统内存管理的一种技术。 它使得应用程序认为它拥有连续可用的内存(一个连续完整的地址空间), 而实际上,它通常是被分隔成多个物理内存碎片, 还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。)

内存映射文件是OS通过mmap在内存中创建一个数据文件,这样就把文件映射到一个虚拟内存的区域。

虚拟内存对于进程来说,是一个物理内存的抽象,寻址空间大小为2^64

操作系统通过mmap来把进程所需的所有数据映射到这个地址空间(红线), 然后再把当前需要处理的数据映射到物理内存(灰线)

当进程访问某个数据时,如果数据不在虚拟内存里, 触发page fault,然后OS从硬盘里把数据加载进虚拟内存和物理内存

如果物理内存满了,触发swap-out操作,这时有些数据就需要写回磁盘, 如果是纯粹的内存数据,写回swap分区,如果不是就写回磁盘。

mongodb java 存文件 mongodb怎么存储文件_数据文件


MongoDB的存储模型

mongodb java 存文件 mongodb怎么存储文件_数据库_02

  • 有了内存映射文件,要访问的数据就好像都在内存里面,简单化了MongoDB访问和修改数据的逻辑
    MongoDB读写都只是和虚拟内存打交道,剩下都交给OS打理 (mongoDB的高效数直接通过系统高效的VM系统来实现的)
    虚拟内存大小=所有文件大小+其他一些开销(连接,堆栈)
    如果journal开启,虚拟内存大小差不多翻番(Journaling是MongoDB中非常重要的一项功能,类似于关系数据库中的事务日志。Journaling能够使MongoDB数据库由于意外故障后快速恢复)

使用MMF的好处1:不用自己管理内存和磁盘调度2:LRU策略3:重启过程中,Cache依然在。

使用MMF的坏处2:RAM使用会受磁盘碎片的影响,高预读也会影响2:无法自己优化调度算法,只能使用LRU

mongoDB 文件系统

mongodb java 存文件 mongodb怎么存储文件_mongodb java 存文件_03


extent 组成

在每一个数据文件内,MongoDB把所存储的BSON文档的数据和B树索引组织到逻辑容器“Extent”里面。

一个文件可以有多个Extent
每一个Extent只会包含一个集合的数据或者索引
同一个集合的数据或索引可以分布在多个Extent内。这几个Extent也可以分步于多个文件内
同一个Extent不会又有数据又有索引
结构说明:

lenght

xNext

xPrev

firstRecord

LastRecord

mongodb java 存文件 mongodb怎么存储文件_数据_04


extent每个record 的组成

-> recordlength

xNext

xPrev

document #文件的json内容

mongodb java 存文件 mongodb怎么存储文件_数据_05


磁盘上的文件是有extent构成,分配集合空间的时候也是以extent为单位进行分配的

一个集合有一个或者多个etent
ns文件里面命名空间记录指向那个集合的第一个extent
数据文件与空间分配 当创建数据库时 (其实MongoDB没有显式创建数据库的方法,在向数据库中的集合写入数据时会自动创建该数据库), MongoDB会在磁盘上分配一组数据文件,所有集合,索引和数据库的其他元数据都保存在这些文件里。

数据文件被放在启动时指定的dbpath里,默认放入/data/db下面。

典型的一个文件组织结构如下:

mongodb java 存文件 mongodb怎么存储文件_数据_06


mongod.lock中存储了服务器的进程ID,是一个进程锁定文件。

数据文件是依据所属的数据库命名的。 test.ns是第一个生成的文件(ns扩展名就是namespace的意思), 数据库中的每个集合和索引都有自己的命名空间,每个命名空间的元数据都存放在这个文件里。

默认情况下,.ns文件大小固定在16MB,大约可以存储24000个命名空间。(16mb->24000) 也就是说数据库中的索引和集合总数不能超过24000,该值可以通过mongod的–nssize选项进行定制。 像test.0这样以0开始的整数结尾的文件就是集合和索引数据文件。刚开始的时候,即使只有一条数据,

MongoDB也会预分配几个文件,这种预分配的做法,能让数据

尽可能连续存储,
减少磁盘碎片。
在像数据库添加数据时,MongoDB会分配更多的数据文件。

每个新数据文件的大小都是上一个已分配文件的两倍(64M->128M->256M),直到预分配文件大小的上限2G。

此处基于一个假设,如果总数据大小呈恒定速率增长,应该逐渐增加数据文件分配的空间。

当然这个预分配策略也是可以通过–noprealloc关掉,但是不建议在production环境下使用。 默认的local数据库,该数据库不参与replication。

当mongod是一个副本集的成员时,在local数据库中就有一个叫做oplog.rs的预分配的capped集合,

预分配的大小为磁盘空间的5%。这个大小可以通过–oplogSize进行调整。

oplog主要用于副本集Primary和Secondary成员见的replication,它的大小限制了两个副本集之间, 在重新完全同步之前,允许多长时间不同步。 journal目录,journal功能2.4版本默认是开启的。

可以使用db.stats()来确认已使用空间和已分配空间。

使用db.accesslog.stats()确认某个集合的使用量 复制代码