简介
MongoDB 是一种流行的文档型数据库。它的高度灵活性和可扩展性使得它成为了前端开发者的首选。本篇文章将深入探讨 MongoDB 的内部存储结构,以及介绍一些性能优化技巧。
内部存储结构
MongoDB 采用了 B-树(B-tree)和 LSM-树(Log Structured Merge Tree)和两种基本的存储结构。B-树的出现使得 MongoDB 可以在单机上处理大部分的查询请求。而 LSM-树则支持了高并发时 MongoDB 的写入请求。
B-树
B-树是一种多叉平衡查找树,它的每个节点可以存放多个值,并且节点键值已经按照升序排列。B-树的每个节点有一个定长的存储块大小来存放数据,这使得 MongoDB 可以在磁盘上面直接存储 B-树。
显然,B-树的查询时间复杂度为 $\log n$,其中 $n$ 为节点数。在 MongoDB 中,使用 B-树存储索引和文档的 id。除了最后一层的节点外,其他节点都是包含多个名值对的文档。
LSM-树
LSM-树是一种基于磁盘进行数据存储和读取的数据结构。它将与内存相比较较慢的磁盘操作转换为更快的内存操作来进行读取和写入,从而提高了 MongoDB 的写入性能。
LSM-树将数据存储在多个阶段中,每个阶段的大小都逐渐增大。在每个阶段中,数据可能重叠或冲突,因此通常需要合并。合并阶段是 LSM-树最慢的部分。当磁盘上的数据太多时,合并过程就会终止。
在 MongoDB 中,LSM-树用于存储集合数据。每个文档在插入到 LSM-树之前都会被拆分成多个块,每个块只在 LSM-树中的一层中存储,这样可以提高查询性能。在查询阶段,所有与查询匹配的块都会被读取并合并为一个文档。
性能优化技巧
MongoDB 的高可用性,可扩展性和可靠性是为开发者所熟知的。但是,为了使 MongoDB 正常运行,我们需要时常优化性能。
合适的索引
索引在 MongoDB 中扮演着基本角色。通过对数据的索引,MongoDB 能够在查询数据时更加高效。在 MongoDB 中,一个索引会占据特定的内存和磁盘空间,同时会对写入性能进行影响。
以下是优化索引的一些技巧:
- 创建索引时,确保它能被查询使用。通过查询计划查看索引是否有用,然后评估其影响。
- 将根据查询条件进行排序的索引放在前面。例如,如果查询需要按数值类型进行排序,那么可以考虑使用降序或升序的索引列。
- 除非是必需的,否则尽量避免在一个大的集合上建立索引,这可能会极大地降低写入性能。
例如,在 MongoDB 中,使用 .explain("allplansexecution")
命令可以计算可能使用的查询计划,并帮助我们优化查询:
// javascriptcn.com 代码示例 // 查询语句 db.collection.find({ "name": "John" }, { "age": 1 }).explain("allPlansExecution"); // 查询计划输出 { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.collection", "indexFilterSet" : false, "parsedQuery" : { "name" : { "$eq" : "John" } }, "winningPlan" : { "stage" : "PROJECTION", "transformBy" : { "age" : 1 }, "inputStage" : { "stage" : "IXSCAN", "keyPattern" : { "name" : 1 }, "indexName" : "name", "isMultiKey" : false, "multiKeyPaths" : { "name" : [ ] }, ... } }, "allPlans" : [ ... ], "serverInfo" : { "host" : "mydb.example.net", "port" : 27017, "version" : "3.6.17", "gitVersion" : "ff065b9a9b5e00e04ae80e3ee793f5fef5ec7eb1" } }, "ok" : 1 }
合适的数据类型
MongoDB 中的文档可以使用不同的数据类型。在设计数据模型时,需要考虑使用合适的数据类型以最大化性能。
以下是使用合适的数据类型的示例:
- 避免在查询条件中使用字符串等大数据类型。
- 当使用 $lt、$lte、$gt 和 $gte 比较查询时,尽量使用类型为 ISODate 和 NumberLong 的键,而不是字符串类型。
- 将嵌套的数据存储在嵌套的文档中,并使用嵌套文档而不是数组。
- 对于数据模型中需要频繁查询的数字类型,使用 NumberInt 和 NumberLong 而不是默认情况下的 NumberDouble 类型。
数据合并
在 MongoDB 中,数据合并是一种将大量小文档合并成较少大文档的技术。这有助于提高使用存储和写入性能,但会对读取性能产生一定的影响。
以下是数据合并的示例:
// javascriptcn.com 代码示例 // 合并前的数据 { "_id": ObjectId("111111111111"), "name": "John", "age": 23 } { "_id": ObjectId("111111111112"), "name": "David", "age": 24 } { "_id": ObjectId("111111111113"), "name": "Mary", "age": 25 } // 合并后的数据 { "_id": ObjectId("111111111114"), "name": ["John", "David", "Mary"], "age": [{"name": "John", "age": 23}, {"name": "David", "age": 24}, {"name": "Mary", "age": 25}] }
上述合并操作能够将原本的三个文档合并为一个文档,减少了 MongoDB 对磁盘的操作次数。
总结
本文对 MongoDB 的 B-树和 LSM-树的内部存储结构进行了详细说明,并介绍了一些性能优化技巧。为了优化 MongoDB 的性能,需要使用适当的索引和数据类型,并使用数据合并来提高存储和写入性能。这些技巧可以帮助我们更好地理解 MongoDB 的内部工作原理,并针对实际应用场景提供更加有效的性能优化方案。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/653e33107d4982a6eb7c348f