打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
MongoDB内部架构


我坚信数据库系统在其存储层面上具有相似的核心基础,了解这些基础可以让我们客观地比较不同的DBMS。例如,MongoDB中的文档存储方式与MySQL或PostgreSQL存储行的方式没有什么不同。

所有数据都存储在磁盘上,关键是以尽可能少的I/O操作从磁盘高效地获取所需数据,其余的就是API。

对我来说,数据库由前端和后端两部分组成。前端是API(SQL vs get/set)和数据格式(表 vs 文档)。后端是存储引擎,定义了数据在磁盘上的存储方式、索引、WAL等。

SQL和NoSQL之间的核心区别在于前端。

在本文中,我将讨论MongoDB内部架构的演变,重点是文档的存储和检索,特别关注索引存储表示。我假设读者对数据库工程的基本原理已经很熟悉,如索引、B+树、数据文件、WAL等。

让我们开始吧。

MongoDB的组件

为了探索MongoDB的架构,我认为了解数据库系统的一些组件非常重要。

让我们从探索MongoDB中我认为与我们讨论相关的基本组件开始。

文档和集合

MongoDB是一种基于文档的非关系型数据库,这意味着它不处理基于关系模式的表,而是处理非关系模式的文档。

用户将JSON文档提交到MongoDB,它们在内部以BSON(二进制JSON)格式存储,以实现更快速和高效的存储。MongoDB将BSON转换回JSON以供用户查询和使用。下面是MongoDB对BSON的定义:

BSON代表"Binary JSON",它的确是为此而发明的。BSON的二进制结构编码了类型和长度信息,这使得它比JSON更快速地进行遍历。

由于文档可能很大,MongoDB有时会对它们进行压缩以进一步减小大小。MongoDB并不总是压缩BSON,我们稍后会详细探讨这一点。

用户创建集合(类似于RDBMS中的表),集合可以存储多个文档。由于MongoDB是无模式(schema-less)数据库,集合可以存储具有不同字段的文档,这是可以接受的。用户可以提交包含集合中任何文档中从未出现过的字段的文档。这个特性使得MongoDB对开发人员非常有吸引力,但也是最容易被滥用的特性之一。

_id索引

当在MongoDB中创建一个集合时,会同时创建一个代表文档ID的主键_id,并创建一个B+树索引,以实现最佳的搜索效果。_id字段唯一标识文档,并可以用它来查找文档。

_id的类型是objectId,它是一个12字节的字段。它的大小较大是因为MongoDB使用它来在机器或分片之间唯一标识文档,以实现可扩展性。在这里可以阅读有关objectId结构的更多信息。

我还认为用户可以使用自己选择的值来覆盖_id字段,这可能会使键变得更大。在我们阅读本文时,请记住这一点。

_id主索引用于通过B+树结构将_id映射到BSON文档。随着MongoDB的发展,这种映射本身经历了多个阶段,我们也将进行探讨。

二级索引

用户可以在集合的任何字段上创建二级B+树索引,然后将其指向满足索引条件的BSON文档。这对于使用文档上的不仅仅是默认的_id字段的不同字段进行快速遍历非常有用。如果没有二级索引,MongoDB将不得不进行全集合扫描,逐个查找文档字段。

二级索引的大小取决于两个因素,键的大小(表示被索引字段的大小)和文档指针的大小。我们将看到MongoDB的不同版本和存储引擎在这方面有很大的差异。

现在我们了解了MongoDB的主要组件,让我们探索MongoDB内部架构的演变过程。

原始的 MongoDB 架构

当MongoDB首次发布时,它使用的存储引擎称为MMAPV1(Memory Map files)。在MMAPV1中,BSON文档直接以未压缩的形式存储在磁盘上,而_id主键索引则映射到一个称为Diskloc的特殊值。Diskloc是一对32位整数,表示文件编号和文件在磁盘上的偏移量,即文档所在位置。

当您使用_id获取文档时,B+树主键索引用于查找Diskloc值,然后使用文件和偏移量直接从磁盘读取文档。

正如您可能猜到的那样,MMAPV1具有一些限制。虽然使用文件和偏移量的Diskloc是一种令人惊奇的O(1)查找文档的方法,但在插入和更新文档时,维护它是困难的。当您更新文档时,文档的大小增加,导致偏移量值发生变化,这意味着在该文档之后的所有Diskloc偏移量都需要进行更新。MMapv1的另一个主要限制是写操作的单个全局数据库锁,这意味着每个数据库只能有一个写入者,显著降低了并发写入的速度。

在这个图片中,您可以看到B+树的叶子页被链接在一起,以便进行有效的范围扫描,这是B+树的特性。

MongoDB确实对MMapv1进行了改进,使其成为集合级别锁(类似于表级别锁),但后来在4.0版本中弃用了MMapv1,并采用了新的默认存储引擎WiredTiger。

MongoDB WiredTiger 架构

在2014年,MongoDB收购了WiredTiger并将其作为默认的存储引擎。WiredTiger具有许多特性,如文档级锁定和压缩。这使得两个并发写操作可以同时更新同一集合中的不同文档,而不需要串行化,这是在MMAPV1引擎中不可能实现的。WiredTiger中的BSON文档经过压缩,并存储在一个隐藏的索引中,其中叶子节点是recordId和BSON对。这意味着可以通过较少的I/O获取更多的BSON文档,使得WiredTiger中的I/O更加高效,提高了整体性能。

主索引_id和二级索引已经改为指向recordId(一个64位整数),而不是Diskloc。这类似于PostgreSQL的模型,其中所有索引都是二级索引,并直接指向堆上的tupleid。

然而,这意味着如果用户查找文档的_id,Mongo会使用主索引找到recordId,然后在隐藏的WT索引上进行另一次查找以找到BSON文档。写操作也是同样的情况,插入一个新文档需要更新两个索引。

双重查找的代价会消耗CPU、内存、时间和磁盘空间来存储主索引和隐藏的聚集索引。对于二级索引也是如此。我不禁想起Discord的一篇博客文章,他们为什么从Mongo迁移到Cassandra,其中一个原因是他们的数据文件和索引could no longer fit RAM。存储两个索引可能加剧了他们的问题,但我可能是错误的。

尽管在这种架构中,_id的额外I/O和双重索引存储会增加开销,但主索引和二级索引的大小仍然是可预测的。recordId是一个相对较小的64位整数。请记住这一点,因为这将再次发生改变。

集群集合架构

集群集合是Mongo中引入的全新功能,于2022年6月推出(参见此链接)。聚集索引是一种索引,查找操作可以直接获取所需的所有内容,所有字段都存储在叶子节点中,从而实现了在数据库系统中通常称为"仅索引扫描"的操作。聚集集合在Mongo 5.3中引入,将主索引_id转变为聚集索引,其中叶子页包含BSON文档,不再使用隐藏的WT索引。

这样,在_id上的查找操作直接返回BSON文档,提高了使用_id字段的工作负载的性能。不再需要进行第二次查找。

由于数据已经移动,二级索引需要指向_id字段而不是recordId。这意味着二级索引仍然需要进行两次查找,一次在二级索引上找到_id,另一次在主索引_id上找到BSON文档。这和非聚集集合中的操作方式没有什么不同,只是我们找到的是recordId而不是_id。

然而,这带来了一个问题,二级索引现在将12字节(是字节而不是位)作为值存储到它们的键中,这会显著增加聚集集合上所有二级索引的大小。更糟糕的是,一些用户可能会定义自己的_id,它的长度可能超过12字节,进一步增加了二级索引的大小。因此,在数据建模过程中要注意这一点。

这将Mongo的架构变得类似于MySQL InnoDB,其中二级索引指向主键。但与MySQL不同的是,Mongo至少可以选择是否对集合进行聚集。这实际上是一个相当不错的权衡。

总结

数据库系统在其内部存储模型方面具有相同的基本原理。我非常喜欢这一点,因为它消除了繁杂的内容,并使我能够以可预测的方式回答与性能相关的问题。对于理解基本原理的工程师来说,每个数据库都声称自己是最好、最快和最可扩展的市场宣传手册已经失去了影响力。

在本文中,我讨论了MongoDB内部架构的演变。在MongoDB中,集群集合是一个有趣的功能。然而,必须谨慎使用,因为较多的二级索引会导致索引大小变大,从而更难将其放入内存以实现更快的遍历。请参考MongoDB关于集群集合的文档。

参考资料

https://groups.google.com/g/wiredtiger-users/c/qQPqhjxyU00

http://smalldatum.blogspot.com/2021/08/on-storage-engines.html?m=1

http://smalldatum.blogspot.com/2015/07/linkbench-for-mysql-mongodb-with-cached.html

https://groups.google.com/g/mongodb-dev/c/8dhOvNx9mBY

https://www.mongodb.com/docs/upcoming/core/clustered-collections/#clustered-collections

https://jira.mongodb.org/browse/SERVER-14569

https://www.mongodb.com/docs/v4.4/reference/operator/meta/showDiskLoc/

https://github.com/mongodb/mongo/commit/374438c9134e6e31322b05c8ab4c5967d97bf3eb

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
什么是MongoDB?简介、架构、功能和示例 | MongoDB中文社区
mongo nosql
MongoDB是什么,怎么用?看完你就知道了
MongoDB 索引限制 | 菜鸟教程
mongodb小结 – 不周山
MongoDB官方发布的每次重大修改的版本特性总结
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服