3

我有一组用户。每个用户都有一个经常更新的“地理位置”字段(每次用户显着移动时)。由于我希望在更新时在文档级别而不是集合级别上实现并发,因此我使用的是 WiredTiger 存储引擎。

我了解到,使用 WiredTiger,文档中的每次更新都会创建一个新文档:

http://learnmongodbthehardway.com/schema/wiredtiger/

WiredTiger 不支持就地更新

然而,这篇文章还说“即使 [WiredTiger] 不允许就地更新,它仍然可以在许多工作负载上表现得比 MMAP 更好”。这是什么意思?使用 WiredTiger 时必须注意的确切含义是什么?例如,如果没有就地更新,数据库大小会快速增长吗?还有其他需要注意的事情吗?

我还了解到 MongoDB 3.6 中的 WiredTiger 添加了存储增量的功能,而不是重写整个文档 ( https://jira.mongodb.org/browse/DOCS-11416 )。这到底是什么意思?

注意:另外我不明白的是,现在大多数(如果不是全部)硬盘驱动器的扇区大小为 4096 字节,因此您不能仅写入硬盘驱动器 4 字节(例如),而是必须写入完整块4096 字节(所以先读取它,更新其中的 4 个字节,然后再写入)。由于大多数文档通常小于 4096 字节,这是否意味着在任何情况下都需要重写整个文档(即使使用 MMAP)。我错过了什么?

4

1 回答 1

12

使用遗留的 MMAPv1 存储引擎(在 MongoDB 4.2 中删除),就地更新经常被强调为一种优化策略,因为文档的索引直接指向文件位置和偏移量。将文档移动到新的存储位置(特别是如果有许多索引条目要更新)对于 MMAPv1 来说比只需要更新更改的字段的就地更新具有更多的开销。

WiredTiger 不支持就地更新,因为它内部使用了数据库管理系统常用的MVCC(多版本并发控制)。这是对 MMAP 中简单视图的重大技术改进,并允许构建更高级的功能,如隔离级别和事务。WiredTiger 的索引具有一定程度的间接性(引用内部 RecordID 而不是文件位置和偏移量),因此存储级别的文档移动并不是一个重要的痛点。

然而,这篇文章还说“即使 [WiredTiger] 不允许就地更新,它仍然可以在许多工作负载上比 MMAP 执行得更好”。

这意味着尽管 MMAPv1 可能有更有效的就地更新路径,但 WiredTiger 具有其他优势,例如压缩和改进的并发控制。您也许可以构建一个工作负载,仅包含对几个文档的就地更新,这可能在 MMAPv1 中表现更好,但实际工作负载通常更加多样化。确认对给定工作负载的影响的唯一方法是在有代表性的环境中进行测试。

但是,如果您想为未来做计划,那么 MMAPv1 与 WiredTiger 的一般选择是没有实际意义的:WiredTiger 自 MongoDB 3.2 以来一直是默认存储引擎,并且 MMAPv1 不支持一些较新的产品功能。例如,MMAPv1 不支持Majority Read Concern,这反过来意味着它不能用于Replica Set Config Servers(MongoDB 3.4+ 中的分片所需)或Change Streams (MongoDB 3.6+)。MMAPv1 在 MongoDB 4.0 中已弃用,并在 MongoDB 4.2 中删除

使用 WiredTiger 时必须注意的确切含义是什么?例如,如果没有就地更新,数据库大小会快速增长吗?

存储结果取决于几个因素,包括您的架构设计、工作负载、配置和 MongoDB 服务器的版本。MMAPv1 和 WiredTiger 使用不同的记录分配策略,但两者都将尝试使用标记为空闲/可重用的预分配空间。总的来说,WiredTiger 对存储空间的使用效率更高,而且它还具有数据和索引压缩的优势。MMAPv1分配额外的存储空间以尝试优化就地更新并避免文档移动,尽管您可以为工作负载不随时间改变文档大小的集合选择“无填充”策略。

自从在 MongoDB 3.0 中首次引入 WiredTiger 以来,在针对不同工作负载改进和调整 WiredTiger 方面进行了大量投资,因此我强烈建议使用最新的生产版本系列进行测试以获得最佳结果。如果您有关于模式设计和存储增长的具体问题,我建议您在 DBA StackExchange 上发布详细信息以供讨论。

我还了解到 MongoDB 3.6 中的 WiredTiger 添加了存储增量的功能,而不是重写整个文档 ( https://jira.mongodb.org/browse/DOCS-11416 )。这到底是什么意思?

这是一个实现细节,可以针对某些用例改进 WiredTiger 的内部数据结构。特别是,MongoDB 3.6+ 中的 WiredTiger 可以更有效地处理对大型文档的小更改(与以前的版本相比)。WiredTiger 缓存需要能够返回多个版本的文档,只要它们被打开的内部会话(MVCC,如前所述)使用,因此对于具有少量更新的大型文档,存储增量列表可能更有效。但是,如果累积的增量过多(或增量正在更改文档中的大部分字段),则此方法的性能可能不如维护完整文档的多个副本。

当数据通过检查点提交到磁盘时,仍然需要写入文档的完整版本。如果您想了解更多有关内部的信息,请观看 MongoDB Path To Transactions系列视频,了解 MongoDB 4.0 中支持多文档事务的功能开发。

另外我不明白的是,现在大多数(如果不是全部)硬盘驱动器的扇区大小为 4096 字节,因此您不能仅写入硬盘驱动器 4 字节(例如),而是必须写入 4096 的完整块字节(所以先读取它,更新其中的 4 个字节,然后再写入)。由于大多数文档通常小于 4096 字节,这是否意味着在任何情况下都需要重写整个文档(即使使用 MMAP)。我错过了什么?

在不深入了解实现细节并试图解释所涉及的所有移动部分的情况下,考虑不同的方法如何应用于更新许多文档的工作负载(而不是在单个文档级别)以及对内存使用的影响(之前文件写入磁盘)。根据文档大小和压缩等因素,单个 I/O 块可以表示从文档的一小部分(最大大小 16MB)到多个文档的任何地方。

在 MongoDB 中,一般流程是在内存视图(例如,WiredTiger 缓存)中更新文档,在定期刷新到数据文件之前,更改以快速仅附加日志格式持久保存到磁盘。如果 O/S 只需写入已更改的数据块,则接触更少的数据块需要更少的整体 I/O。

于 2018-04-03T06:47:21.580 回答