MongoDB爱好者
垂直技术交流平台

常见问题: MongoDB 存储

  • 存储引擎基础知识
  • 你可以在副本集中混用存储引擎吗?
  • WiredTiger存储引擎
  • MMAPv1存储引擎
  • 我可以手动填充文档以防止更新期间的移动吗?
  • 数据存储诊断

该文档讲述关于MongoDB存储系统的常见问题。

存储引擎基础

什么是存储引擎?

存储引擎是数据库的一部分,负责管理如何在内存和磁盘上存储数据。许多数据库支持多个存储引擎,其中不同的引擎对特定工作负载的性能会更好。例如,一个存储引擎可能为读取繁重的工作负载提供更好的性能,另一个可能支持更高的写入操作吞吐量。

也可以看看 存储引擎

你可以在副本集中混用存储引擎吗?

是的。您可以拥有使用不同存储引擎的副本集成员。

注意

MongoDB 4.0不推荐使用MMAPv1存储引擎。

在设计这些多存储引擎部署时,请考虑以下事项:
每个成员上的oplog可能需要设置不同的大小来负责不同存储引擎之间的吞吐量差异。
如果备份是以从MongoDB快照数据文件的方式,那么备份恢复可能会变得更加复杂:您可能需要维护每个存储引擎的备份。

WiredTiger存储引擎

我可以将现有部署升级到WiredTiger吗?

是。参考:
将单机部署的存储引擎更改为WiredTiger
将副本集的存储引擎更改为WiredTiger
将分片集群的存储引擎更改为WiredTiger

WiredTiger提供的压缩比率是多少?

压缩数据与未压缩数据的比率取决于您的数据和使用的压缩算法库。默认情况下,WiredTiger中的集合数据使用Snappy块压缩 ;也可以使用 zlib 压缩。索引数据默认使用前缀压缩。

我应该将WiredTiger内部缓存设置为多大?

使用WiredTiger,MongoDB同时使用WiredTiger内部缓存和文件系统缓存。

从MongoDB 3.4开始,默认的WiredTiger内部缓存大小是以下两者中的较大者:
– 50%(内存大小 – 1 GB),或
– 256 MB。

例如,在总共4GB 内存的系统上,WiredTiger缓存将使用1.5GB的内存(0.5* (4 GB – 1 GB) = 1.5 GB)。相反,具有总共1.25 GB 内存的系统将为WiredTiger缓存分配256 MB,因为这超过总内存的一半减去1GB字节(0.5 *(1.25 GB – 1 GB) = 128 MB < 256 MB)。

默认情况下,WiredTiger对所有集合使用Snappy块压缩,对所有索引使用前缀压缩。压缩默认设置可在全局级别配置,也可在每个集合和每个索引创建期间单独进行设置。

WiredTiger内部缓存中的数据与磁盘上的数据使用不同表示形式的数据格式:

  • 文件系统缓存中的数据与磁盘格式相同,包括对数据文件进行的任何压缩的好处也是一样的。操作系统使用文件系统缓存来减少磁盘I / O.
  • 在WiredTiger内部缓存中加载的索引具有与磁盘格式不同的数据表示,但仍可利用索引前缀压缩来减少内存使用。索引前缀压缩从索引字段中去除重复的公共前缀。
  • WiredTiger内部缓存中的集合数据是未压缩的,并使用与磁盘格式不同的表示形式。块压缩可以节省大量的磁盘存储空间,但数据必须解压缩才能由服务器操作。

通过文件系统缓存,MongoDB自动使用WiredTiger缓存或其他进程未使用的所有可用内存。

要调整WiredTiger内部缓存的大小,请参阅 storage.wiredTiger.engineConfig.cacheSizeGB–wiredTigerCacheSizeGB。应当避免将WiredTiger内部缓存大小增加到其默认值以上。

注意
storage.wiredTiger.engineConfig.cacheSizeGB限制WiredTiger内部缓存的大小。操作系统将使用可用的空闲内存用于文件系统缓存,这允许压缩的MongoDB数据文件保留在内存中。此外,操作系统将使用任何空闲内存来缓冲文件系统块和文件系统缓存。

为了容纳其他的内存消费者,您可能必须减少WiredTiger内部缓存大小。

默认的WiredTiger内部缓存大小值假定每台计算机只有一个mongod实例。如果单个计算机包含多个MongoDB实例,则应减少该设置以适应其他mongod 实例。

如果您的mongod是运行在无法访问系统中所有可用内存 的容器(例如lxc, cgroups,Docker等)中时,则必须将storage.wiredTiger.engineConfig.cacheSizeGB设置为小于容器中可用内存大小的值。确切的大小取决于容器中运行的其他进程。

要查看有关缓存和缓存淘汰率的统计信息,请参阅从serverStatus命令返回的 wiredTiger.cache字段。

WiredTiger写入磁盘的频率如何?

Checkpoints(检查点)

从版本3.6开始,MongoDB配置WiredTiger以60秒的间隔创建checkpoints(即将快照数据写入磁盘)。在早期版本中,MongoDB在WiredTiger中以60秒的间隔设置检查点,或者在写入2 GB的预写日志(journal)数据时,以先发生者为准。

Journal Data(预写日志数据)

  • MongoDB根据以下间隔或条件写入磁盘:
  • MongoDB每50毫秒将缓冲的预写日志(journal)数据同步到磁盘(从MongoDB 3.2开始)
  • 如果写入操作包括写关注(write concern)的j参数:j: true,则WiredTiger强制同步WiredTiger日志文件。
  • 由于MongoDB使用的预写日志(journal)文件大小限制为100 MB,因此WiredTiger大约每100 MB数据创建一个新的日志文件。当WiredTiger创建新的日志文件时,WiredTiger会同步以前的日志文件。

如何在WiredTiger中回收磁盘空间?

WiredTiger存储引擎在删除文档时维护数据文件中的空记录列表。此空间可以由WiredTiger重用,但除非在非常特定的情况下,否则不会返回到操作系统。

可供WiredTiger重用的空白空间量反映在db.collection.stats()输出的wiredTiger.block-manager.file字段中(单位为字节)

要允许WiredTiger存储引擎将此空白空间释放到操作系统,您可以对数据文件进行碎片整理。这可以使用compact命令来实现。有关其行为和其他注意事项的更多信息,请参阅compact

MMAPv1存储引擎

已弃用:
MongoDB 4.0弃用了MMAPv1存储引擎,并将在以后的版本中删除MMAPv1。要将MMAPv1存储引擎部署更改为 WiredTiger存储引擎,请参阅:

将单机部署的存储引擎更改为WiredTiger
将副本集的存储引擎更改为WiredTiger
将分片集群的存储引擎更改为WiredTiger

什么是内存映射文件?

内存映射文件是一个文件,其中包含操作系统通过mmap()系统调用放入内存的数据。mmap()从而 将文件映射到虚拟内存区域。内存映射文件是MongoDB中MMAPv1存储引擎的关键部分。通过使用内存映射文件,MongoDB可以将其数据文件的内容视为内存中的内容。这为MongoDB提供了一种访问和操作数据的极其快速和简单的方法。

内存映射文件如何工作?

MongoDB使用内存映射文件来管理和交互所有数据。
内存映射将文件分配给具有直接逐字节相关性的虚拟内存块。MongoDB内存在访问文档时将数据文件映射到内存。未访问的数据未映射到内存。
映射后,文件和内存之间的关系允许MongoDB与文件中的数据进行交互,就像它是内存一样。

MMAPv1写入磁盘的频率如何?

在MMAPv1存储引擎的默认配置中,MongoDB每隔60秒写入磁盘上的数据文件,并且大约每100毫秒写入journal日志文件。

要更改写入数据文件的间隔,请使用 storage.syncPeriodSecs设置。关于journal日志文件写入间隔,请参阅storage.journal.commitIntervalMs设置。

这些值表示完成写入操作与MongoDB写入数据文件或日志文件之间的最长间隔时间。在许多情况下,MongoDB和操作系统会更频繁地将数据刷新到磁盘,因此上述值代表理论上的最大值。

为什么我的数据目录中的文件大于数据库中的数据?

数据目录中的数据文件(/data/db 默认配置中的目录)可能大于插入数据库的数据集。考虑以下可能的原因:

预分配数据文件

MongoDB预先分配其数据文件以避免文件系统碎片,因此,这些文件的大小不一定反映数据的大小。
该storage.mmapv1.smallFiles选项将减小这些文件的大小,如果磁盘上有许多小型数据库,这可能很有用。

oplog

如果mongod是副本集的成员,数据目录包括oplog.rs文件,这是一个在local 数据库中预分配的定容集合(capped)。
在64位安装上,默认分配大约是磁盘空间的5%。在大多数情况下,您不需要调整oplog的大小。有关更多信息,请参阅Oplog大小调整

journal

数据目录包含预写日志文件,它们主要作用是在MongoDB将数据应用到数据库之前将写入操作存储在磁盘上。请参阅 Journaling

空记录

MMAPv1存储引擎在删除文档和集合时维护数据文件中的空记录列表。此空间可以重用于同一数据库中的新记录分配,但默认情况下,MMAPv1不会将此空间返还给操作系统。

要允许MMAPv1存储引擎更有效地重用空记录中的空间,您可以对数据进行碎片整理。要清理碎片,请使用compact命令。compact 需要多达2 GB的额外的磁盘空间来运行。如果磁盘空间严重不足,请不要使用compact 。有关其行为和其他注意事项的更多信息,请参阅 compact

compact仅从集合的MongoDB数据文件中删除碎片,并且不向操作系统返回任何磁盘空间。要将磁盘空间返回到操作系统,请参阅 如何回收磁盘空间?

如何回收磁盘空间?

注意
您无需为MongoDB回收磁盘空间以重用已释放的空间。有关重用已释放空间的信息,请参阅 空记录

对于副本集的secondary节点,您可以通过停止secondary节点重新同步(resync),从成员的数据目录中删除所有数据和子目录,并重新启动secondary节点,来执行成员的重新同步。有关详细信息,请参阅 重新同步副本集的成员

删除未使用的数据库dropDatabase也将删除关联的数据文件并释放磁盘空间。

什么是工作集?

工作集表示应用程序在正常操作过程中使用的数据总体。通常这是总数据大小的子集,但工作集的特定大小取决于数据库的实际使用时间。

如果您运行的查询要求MongoDB扫描集合中的每个文档,则工作集将扩展以包括每个文档。根据物理内存大小,这可能导致工作集中的文档“page out”,或者被操作系统从物理内存中移除。下次MongoDB需要访问这些文档时,MongoDB可能会出现硬缺页(hard page fault)错误。

为获得最佳性能,你的活跃的大多数数据集应该适合内存大小。

什么是缺页(page fault)错误?

使用MMAPv1存储引擎时,可能会发生缺页错误,因为MongoDB会将数据读取或写入当前未位于物理内存中的数据文件。与此对比,当物理内存耗尽且物理内存页面交换到磁盘时,会发生操作系统缺页错误。

如果有空闲内存,则操作系统可以在磁盘上找到该页(page)并直接将其加载到内存中。但是,如果没有空闲内存,操作系统必须:
在内存中找到过时或不再需要的页面,并将该页面写入磁盘。
从磁盘读取请求的页面并将其加载到内存中。

在活跃的系统上,此过程可能需要很长时间,特别是与读取已在内存中的页相比。

有关详细信息,请参阅 缺页错误

软缺页和硬缺页错误有什么区别?

当具有MMAP存储引擎的MongoDB需要访问当前不在活动内存中的数据时,会发生缺页错误。“硬”缺页错误是指MongoDB必须访问磁盘才能访问数据的情况。相反,“软”缺页错误仅将内存页从一个列表移动到另一个列表,例如从操作系统文件缓存移动。

有关详细信息,请参阅 缺页错误

我可以手动填充文档以防止更新期间的移动吗?

在3.0.0版中更改。

使用MMAPv1存储引擎,如果文档大小增加,更新可能会导致文档在磁盘上移动。为了 最小化文档移动,MongoDB使用填充(padding)。

您不必手动填充,因为默认情况下,MongoDB使用 Power of 2 Sized Allocations 自动添加填充。2的指数大小分配( Power of 2 Sized Allocations) 确保MongoDB分配大小为2的指数大小的文档空间,这有助于确保MongoDB可以有效地重用文档删除产生的空间或移动,并在许多情况下减少重新分配的发生。

但是,如果必须手动填充文档,则可以向文档添加临时字段,然后使用$unset操作符删除字段,如以下示例所示。

警告
不要手动填充定容集合(capped collection)中的文档。将手动填充应用于定容集合中的文档可能会破坏复制。此外,如果重新同步MongoDB实例,则不会保留填充。

复制

VAR myTempPadding = [ “aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” ,
“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” ,
“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” ,
“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa” ];
db.myCollection .insert( { _id : 5 , paddingField : myTempPadding } );
db.myCollection.update( { _id : 5 },
{ $ unset : { paddingField : “” } }

db.myCollection.update( { _id : 5 },
{ $ set : { realField : “我可能需要填充的一些文字” } }

也可以看看
记录分配策略

数据存储诊断

如何查看集合的大小?

要查看集合的统计信息(包括数据大小),请使用mongo shell中的db.collection.stats()方法。以下示例为orders集合执行db.collection.stats():
复制
db.orders.stats ();

MongoDB还提供以下方法来返回集合的特定大小信息:
– db.collection.dataSize() 返回集合的未压缩数据大小(以字节为单位)。
– db.collection.storageSize()返回磁盘存储上集合的字节大小。如果集合数据被压缩(wiredTiger默认配置会压缩),则存储大小反映压缩大小,并且可能小于db.collection.dataSize()返回的值 。
– db.collection.totalIndexSize()返回集合的索引大小(以字节为单位)。如果索引使用前缀压缩(wiredTiger默认配置会对索引前缀压缩),则返回的大小反映压缩的大小。

以下脚本打印每个数据库的统计信息:

db.adminCommand("listDatabases").databases.forEach(function (d) {
mdb = db.getSiblingDB(d.name);
printjson(mdb.stats());})

以下脚本打印每个数据库中每个集合的统计信息:

db.adminCommand("listDatabases").databases.forEach(function (d) {
mdb = db.getSiblingDB(d.name);
mdb.getCollectionNames().forEach(function(c) {
s = mdb[c].stats();
printjson(s);
})})

如何检查集合的各个索引的大小?

要查看为每个索引分配的数据大小,请使用 db.collection.stats()方法并检查返回文档中的 indexSizes字段。

如果索引使用前缀压缩(wiredTiger默认配置会对索引前缀压缩),则该索引的返回大小反映压缩大小。

如何获取有关数据库存储使用的信息?

mongo shell中的db.stats()方法返回“活跃”数据库的当前状态。有关返回字段的说明,请参见 dbStats输出

原文链接:https://docs.mongodb.com/manual/faq/storage/

译者:钟秋
BBD技术经理,资深架构师
MongoDB中文社区联席主席
有丰富项目中应用MongoDB经验,熟悉MongoDB相互模式设计及性能优化,熟悉大数据相关技术和互联网及大数据应用架构设计。

赞(2)
未经允许不得转载:MongoDB中文社区 » 常见问题: MongoDB 存储

评论 抢沙发

评论前必须登录!