Code前端首页关于Code前端联系我们

MongoDB 和 WiredTiger 的简单性

terry 2年前 (2023-09-26) 阅读数 189 #数据库

MongoDB 是主要的 NoSQL 数据库之一。与关系型数据库和其他NoSQL不同,MongoDB采用面向文档的数据存储方式,以类似JSON的方式将数据存储在磁盘上。由于项目中的一些历史问题,作者在最近的工作中经常需要与MongoDB打交道,这也是本文出现的原因。

MongoDB 和 WiredTiger浅入浅出

虽然之前就了解过MongoDB,但真正在项目中大规模使用还是第一次。使用过程中出现了很多问题。不过,这里我们主要关注MongoDB中的一些重要概念。介绍原理并与 MySQL 等传统关系数据库进行比较,让读者评估两者之间的优缺点。

概述

虽然 MongoDB 也是一个数据库,但它与传统的 RDBMS 有显着不同。许多开发人员相信,或者被引导相信,像 MongoDB 这样的无模式数据库比 RDBMS 具有巨大的优势。这种判断实际上是一种误解;因为数据库的性能不仅与数据库本身的设计有关,还与开发者对表和索引结构的设计、存储引擎的选择以及业务有巨大的关系,如果你认为仅仅通过更换数据库,就可以实现性能数量级的提升,这还太年轻。

MongoDB 和 WiredTiger浅入浅出

架构

现有的流行数据库实际上具有非常相似的架构。 MongoDB实际上在架构上与MySQL类似。下层采用“可插拔”的存储模块来满足用户的不同需求。

MongoDB 和 WiredTiger浅入浅出

用户可以根据表中的数据特征选择不同的存储系统,并可以在同一个MongoDB实例中使用;最新版本的 MongoDB 采用 WiredTiger 作为默认存储引擎,WiredTiger 提供不同粒度的并发管理和压缩机制,为不同类型的应用提供最佳的存储性能和效率。

各种存储模块的最顶层是MongoDB数据模型和查询语言。与关系数据库不同,MongoDB 创建了不同的查询语言,因为它的数据存储与 RDBMS 完全不同。 ;虽然MongoDB查询语言非常强大,支持很多功能并且是可编程的,但是它包含的内容非常复杂,API设计也不是很优雅,所以还是需要一定的学习成本。对于长期使用 MySQL 的开发人员来说,本书肯定需要一些时间来适应。

db.collection.updateMany(
   <filter>,
   <update>,
   {
     upsert: <boolean>,
     writeConcern: <document>,
     collation: <document>
   }
)

查询语言的复杂性是由于 MongoDB 支持多种数据类型。每个数据记录,即文档,也具有非常复杂的结构。在设计上没有办法避免这种情况,这也是为什么MongoDB开发也是必要的。读者应该花一些时间学习各种 API。

RDBMS 和 MongoDB

MongoDB 使用面向文档的数据模型,这导致许多概念与 RDBMS 有所不同。虽然大体上两者都有对应的概念,但概念之间的细微差别其实也会影响我们对 MongoDB 的理解:

MongoDB 和 WiredTiger浅入浅出

传统的 RDBMS 实际上是使用 Table 格式在逻辑上以二维方式存储数据表不包含任何复杂的数据结构。不过,得益于 MongoDB,它支持嵌套文档、数组、哈希等多种复杂数据结构的使用,因此它最终将所有数据存储为 BSON 数据格式。

RDBMS 和 MongoDB 中的概念有对应​​关系。数据库、表、行和索引的概念在两个数据库中都非常相似,除了最后一个 JOINEmbedded Document❀ 或 之间存在巨大差异。这种差异实际上影响了使用 MongoDB 时集合架构的设计。如果我们在MongoDB中设计集合时遵循与RDBMS相同的思路,那么就需要使用许多“JOIN”语句。并且 MongoDB 不支持“JOIN”。应用程序内这种查询的性能非常非常差。目前,使用嵌入文档实际上可以解决这个问题。虽然嵌入文档会造成很多数据冗余,让我们更新起来很痛苦,但是查询确实非常快。

{
  _id: <ObjectId1>,
  name: "draveness",
  books: [
    {
      _id: <ObjectId2>,
      name: "MongoDB: The Definitive Guide"
    },
    {
      _id: <ObjectId3>,
      name: "High Performance MySQL"
    }
  ]
}

在使用MongoDB时,我们需要忘记RDBMS中设计表的许多规则,同时清楚地思考MongoDB的优势,仔细思考如何设计表以利用MongoDB提供的许多功能来改进查询效率。

数据模型

MongoDB和RDBMS最大的区别在于数据模型设计上有非常明显的差异。不同的数据模型决定了它具有截然不同的属性。 MongoDB 中存储的数据非常灵活。 Schema,我们不需要像RDBMS那样在插入数据之前就决定和定义表中的数据结构。 MongoDB 组合并没有对集合的数据结构进行任何限制,但在实际使用中,同一个集合中的大多数文档都具有相似的结构。

MongoDB 和 WiredTiger浅入浅出

在为 MongoDB 应用程序设计数据模型时,开发人员必须仔细考虑如何表达数据模型之间的关系。 MongoDB 提供了两种不同的方法来表示文档之间的关系:引用和嵌入。

标准化数据模型

参考(Reference)在MongoDB中被称为标准化数据模型。它与 MySQL 外键非常相似。每个文档都可以通过字段xx_id『链接到其他文档:

MongoDB 和 WiredTiger浅入浅出

但是MongoDB中的这种链接在MySQL中是不会直接通过JOIN来查找的。我们必须使用额外的查询来查找与参考匹配的模型,尽管这提供了更大的灵活性。 ,但是由于客户端和MongoDB之间交互(Round-Trip)次数的增加,也会导致查询变慢,甚至非常严重的性能问题。

MongoDB中的References对引用对应的数据模型是否实际存在没有任何限制,因此如果在应用层面对文档之间的关系没有限制,那么可能存在引用指向不同现有文档的问题:

MongoDB 和 WiredTiger浅入浅出

虽然引用存在严重的性能问题,并且在数据库级别删除模型没有限制,但它们提供了一些嵌入式文档无法提供的功能。当我们需要表达 多对多关系或者更大的数据集时,可以考虑使用标准化的数据模型——引用。

嵌入式数据模型

除了与MySQL非常相似的参考之外,MongoDB还凭借其独特的数据存储方式提供了嵌入式数据模型。嵌入式数据模型也被视为非标准数据。模型:

MongoDB 和 WiredTiger浅入浅出

因为MongoDB使用BSON数据格式来存储数据,而嵌入数据模型中的子文档实际上是父文档中的另一个值,但它存储的是对象:

{
  _id: <ObjectId1>,
  username: "draveness",
  age: 20,
  contact: [
    {
      _id: <ObjectId2>,
      email: "i@draveness.me"
    }
  ]
}

嵌入数据模型允许我们存储同一数据记录中具有相同关系的信息,因此应用程序可以更快地搜索和更新相关数据;当我们的数据模型存在“包含”这样的关系或者模型经常需要与文章、评论等其他模型一起展示(查询)时,那么就可以考虑使用嵌套关系来设计数据模型。

总之,使用insert可以让我们在更少的请求中获取更多的相关数据,为读操作提供更高的性能,同时也为在同一个写请求中同时更新相关数据提供支持。

底层WiredTiger MongoDB存储引擎可以保证对同一个文档的操作是原子的,任何写操作都不能原子地影响多个文档或多个集合。

主键和索引

本节我们主要介绍MongoDB中不同类型的索引,当然也包括非常重要的字段_id

MongoDB 和 WiredTiger浅入浅出

理解为“ MongoDB 的主键”。此外,还介绍了单字段索引、复合索引、多键索引等类型的索引。

MongoDB中索引的概念其实和MySQL中的索引类似。底层数据结构和底层索引类型几乎相同。它们之间的区别在于MongoDB支持不同类型的数据结构。 ,所以提供更多类型的索引也是理所当然的。

MongoDB 和 WiredTiger浅入浅出

默认索引

MySQL中的每一行数据都有一个主键。数据库中的数据物理存储在以主键为键的文件中;主键除了用于存储数据之外,由于其属性还可以加快数据库查询语句的速度。

MongoDB 中的所有文档也有一个唯一的字段_id。默认情况下,所有文档都使用 12 字节 ObjectId 作为默认索引:前四位

MongoDB 和 WiredTiger浅入浅出

表示当前 _id 三位数机器标识符和两位数时的 Unix 时间戳生成数字处理器标识符,最后是一个三位数计数器,初始化值为随机数;这样替换递增的id,可以在一定程度上解决分布式MongoDB生成唯一标识符的问题,同时保证id的增长。是增量的。

单字段索引(Single Field)

除了默认字段_id之外,我们还可以创建其他单键索引,不仅支持顺序索引,还支持对索引倒置:

db.users.createIndex( { age: -1 } )

MySQL 8.0之前的索引只能按正序排列。 8.0之后引入了倒序索引。单字段索引可以说是MySQL中辅助(二级)索引的子集。它只是为除 _id 之外的每个字段按正向或反向顺序创建索引树。

MongoDB 和 WiredTiger浅入浅出

复合索引(compound)

除了非常简单类型的索引,比如单字段索引,MongoDB还支持由几个不同字段组成的复合索引(Compound Index)。由于 MongoDB 支持对同一个数组进行正确的倒序排列,因此会出现比 MySQL 中的辅助索引更多的情况:

db.users.createIndex( { username: 1, age: -1 } )
db.users.createIndex( { username: 1, age: 1 } )

上面的两个索引是完全不同的,磁盘上的 B+ 树实际上是存储在一个完全不同的索引中。不同的顺序。尽管字段 username 按升序排序,但对于 age,这两个索引的处理完全相反:

MongoDB 和 WiredTiger浅入浅出

这也导致在查找集合中的数据时使用如果正向和反向顺序,实际上将使用不同的索引。因此,创建索引时要考虑使用场景,避免创建不必要的索引。

多键(Multikey)

由于MongoDB支持类似数组的数据结构,因此它还提供了一个称为多键索引的功能,可以对数组中的每个元素进行索引。创建索引其实和单数组索引一样,没有太大区别:

db.collection.createIndex( { address: 1 } )

如果一个数组有值并且是一个数组,那么使用上面的代码会自动为该数组创建一个多键索引,这可以加快数组中元素的搜索速度。

文本索引(Text)

文本索引是MongoDB提供的另一个方便的功能。不过,我在这里只提一下这种类型的索引,并不打算深入讨论它。从性能上来说,如果确实想做全文索引,建议使用Elasticsearch这样更专业的东西,而不是使用MongoDB提供的这个功能。

存储

如何存储数据是一个更重要的问题。我们已经提到,MongoDB 和 MySQL 一样,提供插件存储引擎支持。存储引擎作为MongoDB的主要组件之一,全面负责管理MongoDB数据。

MongoDB 和 WiredTiger浅入浅出

WiredTiger

MongoDB3.2之后,WiredTiger是默认的存储机制。如果您不了解每种存储机制,则不应更改 MongoDB 的默认存储机制;它具有许多优点,例如非常高效的Cache机制:

MongoDB 和 WiredTiger浅入浅出

WiredTiger还支持内存和磁盘索引压缩。压缩过程中还使用前缀压缩来减少 RAM 消耗。在后面的文章中,我们将详细介绍和分析WiredTiger存储库引擎如何存储各种数据。

日志记录

为了保证数据库宕机时MongoDB中数据的持久性,MongoDB采用write-ahead的方式将日志文件写入磁盘;除了日志日志之外,MongoDB还使用检查点来保证数据的一致性,当数据库崩溃时,我们需要检查点和日志文件共同完成数据恢复:

  1. 查找数据集中前一个检查点的标识符;
  2. 在日志文件中找到标识符对应的条目;
  3. 重复相应记录后的所有操作;

MongoDB 每 60 秒或当日志数据写入达到 2GB 时设置一个检查点。当然,我们也可以这样写:当传递给参数j: true时,强制同步日志文件。

MongoDB 和 WiredTiger浅入浅出

本文不会介绍日志文件的格式和相关内容。 作者可以在WiredTiger稍后呈现和分析的文章中简要分析其存储格式和其他一些特性。

总结

本文仅简单介绍MongoDB的一些基本特性和数据模型。虽然“无限”扩展是 MongoDB 的一个非常重要的特性,但由于篇幅限制,我们没有包含任何与 MongoDB 集群相关的信息,但我会在后面的文章中专门介绍多个 MongoDB 实例如何协同工作。

这里我想说的是,如果读者的你也收到过类似的说法,说MongoDB的性能比MySQL好很多,但是在使用MongoDB的过程中,你仍然遵循以前的RDBMS对数据库的设计方法,那么相信表现最终将取决于结果。不会有很大的改善,但也可能不是增加,而是减少;只有真正理解MongoDB的数据模型,并根据业务需求进行设计,才能充分利用内嵌文档等特性,提升MongoDB的性能。

关注:Dravness·GitHub

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门