mongodb主要利用存储引擎来管理数据。mongodb为我们提供了多种存储引擎,我们可以根据不同需求来选择最合适的一个。
一、mongodb存储引擎种类
mongodb主要有3种存储引擎,分别是:
(1) WiredTiger
(2) MMAPv1
(3) In-Memory
下面分别介绍这三种存储引擎。
二、WiredTiger
MongoDB 3.0版本开始启用,64位版本。
MongoDB 3.2 成为默认存储引擎
(1) 文档级别的并发
WiredTiger 对写操作执行文档级别的并发控制,也就是说,在同一时间,可以修改一个集合中的不同文档。
针对大多数的读写操作,WiredTiger采取
乐观
的并发控制机制。WiredTiger 在全局、数据库和集合级别身上仅使用
意向锁
。当存储引擎检测到两个操作产生冲突时,其中
一个操作
会引发一个写冲突,促使mongodb显式的的重新执行该操作。
一些全局的操作,典型的有涉及多个库的短暂操作,需要
实例锁
;
一些像删除集合的操作,还需要
排他锁
。
(2) 快照和检查点
WiredTiger 使用多版本的并发控制(MVCC)。
一个操作开始时,WiredTiger 会为事务提供一个实时的数据快照,快照呈现内存中数据的一致性视图。
将数据写入磁盘时,WiredTiger 以一致性的方式将快照中的所有数据写入磁盘。检查点确保数据文件中所有的数据是一致的,并且包含最后一个检查点。检查点可以作为回复数据的点。mongodb配置WiredTiger 来创建检查点(即将快照数据写入磁盘),创建的时机是:每60秒或者当日志文件达到2G。
在写一个新检查点的过程中,上一个检查点仍然是有效的,如果写的过程中出现宕机或者其他异常,一旦重启成功,mongodb会从上一个有效的检查点恢复数据。
当WiredTiger的元数据表关联到新的检查点时,该新的检查点才是可访问和持久的,此时,会释放老检查点的页。
(3) 日志文件系统Journal
默认提交时间间隔为100毫秒。
WiredTiger使用预写事务日志与检查点结合的方式来确保数据持久化。即开启Journal功能,Journal会记录在两个检查点之间的所有修改,当mongodb突然退出并重启之后,WiredTiger会根据Journal中的记录恢复最后一个检查点之后发生的数据修改。
Journal用snappy库压缩。用storage.wiredTiger.engineConfig.journalCompressor指定其他的压缩算法或者不采用压缩策略。WiredTiger 最小的日志记录大小是128字节,如果记录为128字节或者更小,则不会压缩该条记录。
将storage.journal.enabled设置为False,则不会启动Journaling。
对单实例而言,如果禁用journal意味着我们会丢失mongodb异常终止后上一个有效检查点之后的所有数据。对于复制集(主从的mongodb集群)中的一些成员来说,复制过程可能提供充分的持久性保证。
(4) 压缩
使用WiredTiger存储引擎,mongodb支持压缩所有集合和索引。压缩以使用额外的CPU为代价来降低存储消耗。WiredTiger 对集合采用块压缩(使用snappy压缩库或者zlib),对索引采用前缀压缩。可以通过配置来更改压缩方式,换用其他压缩算法或者不进行压缩。再建立一个集合或者一个索引的时候,可以为其制定压缩方式。对于大多数的工作负载,默认的压缩设置会平衡存储效率和处理需求。
(5) 内存使用
使用WiredTiger存储引擎,MongoDB 既会使用WiredTiger的内部缓存,也会使用文件系统的缓存.
从MongoDB3.4开始,WiredTiger 内部缓存会使用下面两个数中的较大者:
a. RAM的50% 减去1 GB
b. 256MB
对于文件系统缓存,MongoDB会自动使用除WiredTiger内部缓存以及其他进程占用内存的所有空闲内存。文件系统中的缓存会被压缩。
想要调整WiredTiger的内部缓存, 可以参考storage.wiredTiger.engineConfig.cacheSizeGB和wiredTigerCacheSizeGB.
避免增加WiredTiger内部缓存的大小超过其默认值。
三、MMAPv1
MMAPv1是MongoDB基于内存映射的最初的存储引擎,它在大量的读写和就地更新的任务上有很好的表现。从3.2版本开始,MMAPv1 不在是默认的存储引擎。
注意:big-endian架构不支持MMAPv1存储引擎,如果在大端机器上采用该存储引擎,会报错。
Endian表示数据在存储器中的存放顺序
,采用大端方式 进行数据存放符合人类的正常思维,而采用小端方式进行数据存放利于计算机处理。大端指低地址存放最高有效字节,小端是指低地址存放最低有效字节。
例如:我们要存储一个16进制的数0x31323334;
小端机器的存储方式为:
低地址 –> 高地址
34 33 32 31
大端机器的存储方式为:
低地址 –> 高地址
31 32 33 34
(1) Journal
为了确保所有的数据都被持久化到磁盘,MongoDB默认将所有修改都保存到磁盘的日志文件中,MongoDB写日志文件要比写数据文件频繁。
storage.syncPeriodSecs
:设置数据文件存储时间间隔,默认60秒。
不要在生产环境设置该参数,在几乎所有场景,你都应该用该默认的参数
,如果将该参数设置为0,MongoDB 将不会同步内存映射文件到磁盘。
storage.journal.commitIntervalMs
:默认值为100毫秒或30毫秒,取值范围是0~500毫秒,取值越低,数据持久性越高,但是会牺牲磁盘性能。
默认日志存储时间间隔为100毫秒。但是如果日志文件被存储在于数据文件不同的区块设备上,例如physical volume, RAID device, or LVM volume,默认日志提交间隔会变为30毫秒,此外, when a write operation with j:true is pending, 提交的时间间隔将要变成设置值的三分之一。
在许多情况下,MongoDB 和操作系统更新数据到磁盘要比设置的时间间隔频繁的多,所以,以上的设置值只是理论上的最大值。
当mongod实例异常退出时,MongoDB 可以根据这些数据文件恢复数据库。
(2) 记录存储特性
所有记录都是连续的存储在磁盘上,当一条记录被更新并且超出原来分配的记录大小,则需要重新分配一个记录,之后将文档移动到新记录中,并且更新与文档相关的所有索引。这显然比原地更新记录要花费更多的时间,同时还会导致存储碎片。
从3.0版本开始,分配策略有所改变。
(3) 记录分配策略
从3.0版本开始,MongoDB使用Power of 2 Sized Allocations策略,将一个文档存储到一条记录中,同时记录中还有额外的空间,这些额外的空间允许更新操作带来的文档大小的增长,使得重新分配内存的可能新降到最低。
不同的分配策略支持不同种类的操作:
the power of 2 allocations
:对insert/update/delete操作更加有效; 采用该策略之后,所有记录的大小均为2的n次幂字节。对于大于
2MB
的文档,分配将要向上取整到2MB的倍数。该策略有以下两个关键特性:
- 能重新使用释放的记录来减少碎片。
- 能减少文档移动,但是不能完全消除重新分配的情况。
exact fit allocations(No Padding Allocation Strategy)
:对没有更新和删除操作的集合更理想。
对于那些没有改变文档大小操作或者只有插入操作的集合,我们可以将the power of 2 allocations设置为无效。在3.0版本以前,MongoDB 通过动态计算填充大小来作为文档大小的因素。
(4) 内存使用
采用MMAPv1存储引擎,MongoDB自动使用机器所有空闲的内存作为它的缓存,系统资源监控显示,MongoDB用了许多内存,但是它的使用是动态的,当其他进程需要使用内存时,MongoDB会放弃部分缓存所占用的内存给其他进程。
从技术上来说,操作系统的虚拟内存子系统管理着MongoDB内存,这意味着MongoDB会尽可能多的使用空闲内存,当需要的是有也会切换到磁盘,部署足够的内存来适应工作数据集将会获得做好的性能。
三、In-Memory(内存存储引擎)
(1) 并发性
内存存储引擎使用文档级别的并发控制,可以在同一时刻对不同文档进行更新操作。
(2) 内存使用
内存存储引擎要求它的所有数据必须符合在命令行参数(–inMemorySizeGB)或者在YAML配置文件中设置的内存大小。
默认情况下,内存存储引擎使用物理RAM的50%减去1 GB的内存。
如果一个写操作超出指定的内存大小,mongodb将会报错,错误信息如下:
“WT_CACHE_FULL: operation would overflow cache”
(3) 持久化
内存存储引擎是
非持久性
的,不会持久化数据。非持久化的数据包括应用程序数据,系统数据,例如用户、权限、索引、副本设置配置、分片集群配置等。数据库正常或者异常关闭的情况下,数据均不可恢复。
在采用内存存储引擎,或者其他存储引擎但是关闭了Journal功能是,必须将writeConcernMajorityJournalDefault参数设置为False。
(4) 部署结构
除了作为单实例运行,采用内存存储引擎的mongod实例也可以作为副本集或者分片集群的一部分。
-
作为副本集的一部分
例如,一个有三个成员的副本集,其结构可以为:
两个mongod实例运行在In-Memory存储引擎上
另外一个mongod实例运行在WiredTiger存储引擎上,并将该实例配置为隐藏成员。
在该配置结构下,只有采用IN-memory的数据库实例会被选举为最主要者,客户端只会连接到该实例上。即使两个使用In-memory的数据库实例均死机或者重启,他们都会从运行在WiredTiger的实例上同步数据。隐藏的数据库实例会将用户数据、索引和其他复制配置信息都持久化到磁盘上。 -
作为分片集群的一部分
例如,一个有三个成员的分片集群,其结构可以为:
两个mongod实例运行在In-Memory存储引擎上
另外一个mongod实例运行在WiredTiger存储引擎上,并将该实例配置为隐藏成员。
为分片加inmem标签。如果该分片的名字为”shardC”,则连接mongos ,然后运行sh.addShardTag(“shardC”, “inmem”),对于其他的分片,分别添加一个persisted 标签,
sh.addShardTag(“shardA”, “persisted”)
sh.addShardTag(“shardB”, “persisted”)
对于每个应该驻留在内存中的分片集合,分配给整块范围的inmem标签。
sh.addTagRange(“test.analytics”, { shardKey: MinKey }, { shardKey: MaxKey }, “inmem”)
对于每个应该驻留在持久化中的分片集合,分配给整块范围的persisted标签。:
sh.addTagRange(“salesdb.orders”, { shardKey: MinKey }, { shardKey: MaxKey }, “persisted”)
对于内存中的分片,创建一个数据库或者移动一个数据库。