MongoDB存储结构
MongoDB目前支持的存储引擎为内存映射引擎
在MongoDB内部,每个数据库都包含一个.ns 文件和一些数据文件,而且这些数据文件会随着数据量的增加而变得越来越多。nssize选项可以设置dbname.ns文件的大小。
MongoDB 内部有预分配空间的机制,每个预分配的文件都用0 进行填充,由于有了这个机制, MongoDB 始终保持额外的空间和空余的数据文件,从而有效避免了由于数据暴增而带来
的磁盘压力过大的问题。由于表中数据量的增加,数据文件每新分配一次,它的大小都会是上一个数据文件大小的2倍,每个数据文件最大2G。这样的机制有利于防止较小的数据库浪费过多的磁盘空间,同
时又能保证较大的数据库有相应的预留空间使用。即文件集序号依次为0,1,2....n大小依次为64M,128M,256M..一直到2G,以后就一直为2G大小的创建下去。
文件集在内部被切分成多个数据域,数据域之间使用双向链表连接。不同数据域的存储类别用命名空间区别,每个集合都会有自己独立的命名空间,每个索引也有自己独立的命名空间。
在mongodb里,每个collection都具有一个命名空间,名字为dbname.collection_name。
名字空间和盘区
每一个数据库都由多个名字空间组成,每一个名字空间存储了相应类型的数据。数据库中的每一个Collection都有各自对应的名字空间,索引文件同样也有名字空间。所有名字空间的元数据都存储在.ns文件中。
每个命名空间可以包含多个不同的盘区,这些盘区并不是连续的。与数据文件的增长相同,每一个命名空间对应的盘区大小的也是随着分配的次数不断增长的。这样做的目的是为了平衡命名空间浪费的空间与保持某一个命名空间中数据的
连续性.$freelist,这个命名空间用于记录不再使用的盘区(被删除的Collection或索引)。每当命名空间需要分配新的盘区的时候,都会先查看$freelist 是否有大小合适的盘区可以使用,这样就回收空闲的磁盘空间。
每个数据库都有一个以 dbname.ns命名的命名空间文件,该文件中保存的是一个 哈希表节点数组,存储的是每个命名空间的元数据,包括其大小,域的个数,第一个域的位置,最后一个域的位置,被删除的域的链表、索引信息等。
dbname.<#>系列文件存储了每个库的所有数据,其文件格式为
--------------------------------------------
DataFileHeader
--------------------------------------------
Extent (for a particular namespace)
Record
...
Record (some chained for unused space)
--------------------------------------------
more Extents...
--------------------------------------------
DataFileHeader是数据文件的头部,后面的部分为Extent。文件空间的分配以Extent为单位。每个命名空间的所申请的Extent形成一个双向链表,
表头和表尾存在命名空间信息里。Record即记录,在Extent里分配,每个Extent里的所有Record形成一个双向链表,表头和表尾存在Extent头部。
可以想到,对命名空间的所有Record的遍历方法为:遍历Extent链表,对每个Extent,遍历其Record链表。空闲的Record(Extent里剩余的空间、或者Record被删除),
称作DeleteRecord,根据其大小,形成19个单向链表(表头也存在命名空间里)。可以想到,申请一个Record的方法:先从空闲的Record里面找;如果找不到,则分配新的Extent。
当一个命名空间被删除的时候,它的所有的Extent都会挂到名为$freelist的collection的Extent链表中。那么,分配Extent的时候,会先从$freelist的Extent链表中寻找。如果找不到,就申请新的Extent。
MongoDB 的数据分块称为 chunk。每个 chunk 都是 Collection 中一段连续的数据记录,通常最大尺寸是 200MB,超出则生成新的数据块。
为了将一个特定的collection 存储在多个shard 中,需要为该collection 指定一个shard key,例如{age: 1} ,shard key 可以决定该条记录属于哪个chunk。Config Servers 就是用来存储:
所有shard 节点的配置信息、每个chunk 的shard key 范围、chunk 在各shard 的分布情况、该集群中所有DB 和collection 的sharding 配置信息。
//////////////////////////////////////
每一个数据库都有自己独立的文件。如果开启了directoryperdb选项,那你每个库的文件会单独放在一个文件夹里。
数据库文件在内部会被切分成单个的块,每个块只保存一个名字空间的数据。在MongoDB中,名字空间用于区分不同的存储类别。比如每个collection有一个独立的名字空间,每个索引也有自己的名字空间。
在一个块中,会保存多条记录,每条记录是BSON格式的,记录与记录之间通过双向链表进行连接。(因为是MMAP,所以文件是内存的映射).
索引数据也存在数据文件中,不过索引是被组织成B Tree结构,而不是双向链表。
对每个数据库,有一个命名空间文件,用于保存每个名字空间对应的元数据。我们通过查询这些元数据来找到对应的名字空间的存储块位置。如果你开启了jorunaling日志,那么还会有一些文件存储着你所有的操作记录。
总结如下:
1、每个数据库都有响应的数据文件和命名空间文件。
2、数据文件从64M开始,新的数据文件比上一个文件大一倍,最大数据文件为2G
3、文件使用MMAP进行内存映射,会将所有数据文件映射到内存中,这里的内存指虚拟内存,只有访问到这块数据才会交换到物理内存。
4、64位机器上最多可以表示128TB的空间
5、每个数据文件被分成一个一个的数据块用来存储数据,块与块之间是用双向链表连接
6、在名字空间文件中(数据库名.ns),保存的是一个 hash table,保存了每个名字空间的存储元数据信息,包括其大小,块数,第一块和最后一块的位置,被删除的块的链表及其索引信息
7、.ns中记录的数据信息的位置通过DiskLoc数据结构进行存储,存储了数据文件编号和块在文件中的位置
8、对每一个块(Extent)来说,其头部包含以下元数据:
块的位置、上一个和下一个块的位置、块中第一条和最后一条记录的位置指针。其他部分用于存储数据,数据之间通过双向链来进行连接。
9、索引是BTree的存储结构。叶子节点存的是_id的值和地址。
四、 MongoDB数据文件的内部结构
每个数据库都有一个.ns文件和若干个数据文件组成(.0,.1,.2,.....),其中.ns文件为16M,而.0文件16M,.1文件32M,往后则翻倍,最大值为2G,这样可以让小数据库不浪费太多的空间,大数据库能够使用磁盘上连续的空间。
数据库里面的每个集合和索引都对应着命名空间。
这是local数据库中的命名空间,可以看到集合,固定集合(capped collection),索引都有自己的命名空间。
.ns文件记录着若干个集合命名空间和索引命名空间。
一个集合命名空间又有多个数据域(extent),集合命名空间里存储着集合的元数据,比如集合名称,集合的第一个数据域和最后一个数据域的位置等等。而一个数据域由若干条文档(document)组成,每个数据域都有一个头部,记录着第一条文档和最后一条文档的为知,以及该数据域的一些元数据。extent之间,document之间通过双向链表连接。
索引的存储数据结构是B树,索引命名空间存储着对B树的根节点的指针。MongoDB数据内部结构图如下(图片来自NoSQLFan)