存储引擎(Storage Engine)是MongoDB的核心组件,负责管理数据如何存储在硬盘(Disk)和内存(Memory)上。从MongoDB 3.2 版本开始,MongoDB 支持多数据存储引擎(Storage Engine),MongoDB支持的存储引擎有:WiredTiger,MMAPv1和In-Memory。
从MongoDB 3.2 版本开始,WiredTiger成为MongDB默认的Storage Engine,用于将数据持久化存储到硬盘文件中,WiredTiger提供文档级别(Document-Level)的并发控制,检查点(CheckPoint),数据压缩和本地数据加密( Native Encryption)等功能。
MongoDB不仅能将数据持久化存储到硬盘文件中,而且还能将数据只保存到内存中;In-Memory存储引擎用于将数据只存储在内存中,只将少量的元数据和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要Disk的IO操作,就能获取索取的数据,In-Memory存储引擎大幅度降低了数据查询的延迟(Latency)。
一,指定MongoDB实例的存储引擎
mongod 参数: --storageEngine wiredTiger | inMemory
指定Storage Engine的类型,
- 如果参数值是wiredTiger,MongoDB使用的存储引擎是WiredTiger,将数据持久化存储在Disk Files中;
- 如果参数值是inMemory,MongoDB使用的存储引擎是In-Memory,将数据存储在内存中;
- 从MongoDB 3.2 版本开始,MongoDB默认的存储引擎是WiredTiger;
二,WiredTiger 存储引擎将数据存储到硬盘文件(Disk Files)
WiredTiger和MMAPv1都用于持久化存储数据,相对而言,WiredTiger比MMAPv1更新,功能更强大。
1,文档级别的并发控制(Document-Level Concurrency Control)
MongoDB在执行写操作时,WiredTiger 在文档级别进行并发控制,就是说,在同一时间,多个写操作能够修改同一个集合中的不同文档;当多个写操作修改同一个文档时,必须以序列化方式执行;这意味着,如果该文档正在被修改,其他写操作必须等待,直到在该文档上的写操作完成之后,其他写操作相互竞争,获胜的写操作在该文档上执行修改操作。
对于大多数读写操作,WiredTiger使用乐观并发控制(optimistic concurrency control),只在Global,database和Collection级别上使用意向锁(Intent Lock),如果WiredTiger检测到两个操作发生冲突时,导致MongoDB将其中一个操作重新执行,这个过程是系统自动完成的。
For most read and write operations, WiredTiger uses optimistic concurrency control. WiredTiger uses only intent locks at the global, database and collection levels. When the storage engine detects conflicts between two operations, one will incur a write conflict causing MongoDB to transparently retry that operation.
2,检查点(Checkpoint)
在Checkpoint操作开始时,WiredTiger提供指定时间点(point-in-time)的数据库快照(Snapshot),该Snapshot呈现的是内存中数据的一致性视图。当向Disk写入数据时,WiredTiger将Snapshot中的所有数据以一致性方式写入到数据文件(Disk Files)中。一旦Checkpoint创建成功,WiredTiger保证数据文件和内存数据是一致性的,因此,Checkpoint担当的是还原点(Recovery Point),Checkpoint操作能够缩短MongoDB从Journal日志文件还原数据的时间。
当WiredTiger创建Checkpoint时,MongoDB将数据刷新到数据文件(Disk Files)中,在默认情况下,WiredTiger创建Checkpoint的时间间隔是60s,或产生2GB的Journal文件。在WiredTiger创建新的Checkpoint期间,上一个Checkpoint仍然是有效的,这意味着,即使MongoDB在创建新的Checkpoint期间遭遇到错误而异常终止运行,只要重启,MongoDB就能从上一个有效的Checkpoint开始还原数据。
当MongoDB以原子方式更新WiredTiger的元数据表,使其引用新的Checkpoint时,表明新的Checkpoint创建成功,MongoDB将老的Checkpoint占用的Disk空间释放。使用WiredTiger 存储引擎,如果没有记录数据更新的日志,MongoDB只能还原到上一个Checkpoint;如果要还原在上一个Checkpoint之后执行的修改操作,必须使用Jounal日志文件。
3,预先记录日志(Write-ahead Transaction Log)
WiredTiger使用预写日志的机制,在数据更新时,先将数据更新写入到日志文件,然后在创建Checkpoint操作开始时,将日志文件中记录的操作,刷新到数据文件,就是说,通过预写日志和Checkpoint,将数据更新持久化到数据文件中,实现数据的一致性。WiredTiger 日志文件会持久化记录从上一次Checkpoint操作之后发生的所有数据更新,在MongoDB系统崩溃时,通过日志文件能够还原从上次Checkpoint操作之后发生的数据更新。
The WiredTiger journal persists all data modifications between checkpoints. If MongoDB exits between checkpoints, it uses the journal to replay all data modified since the last checkpoint.
3,内存使用
3.1 WiredTiger 利用系统内存资源缓存两部分数据:
- 内部缓存(Internal Cache)
- 文件系统缓存(Filesystem Cache)
从MongoDB 3.2 版本开始,WiredTiger内部缓存的使用量,默认值是:1GB 或 60% of RAM - 1GB,取两值中的较大值;文件系统缓存的使用量不固定,MongoDB自动使用系统空闲的内存,这些内存不被WiredTiger缓存和其他进程使用,数据在文件系统缓存中是压缩存储的。
3.2 调整WiredTiger内部缓存的大小
使用 mongod的参数 --wiredTigerCacheSizeGB 来修改MongoDB实例中WiredTiger内部缓存的大小,计算内部缓存大小的公式是:
- Starting in MongoDB 3.2, the WiredTiger internal cache, by default, will use the larger of either: 60% of RAM minus 1 GB, or 1 GB.
- For systems with up to 10 GB of RAM, the new default setting is less than or equal to the 3.0 default setting
- For systems with more than 10 GB of RAM, the new default setting is greater than the 3.0 setting.
4,数据压缩(Data Compression)
WiredTiger压缩存储集合(Collection)和索引(Index),压缩减少Disk空间消耗,但是消耗额外的CPU执行数据压缩和解压缩的操作。
默认情况下,WiredTiger使用块压缩(Block Compression)算法来压缩Collections,使用前缀压缩(Prefix Compression)算法来压缩Indexes,Journal日志文件也是压缩存储的。对于大多数工作负载(Workload),默认的压缩设置能够均衡(Balance)数据存储的效率和处理数据的需求,即压缩和解压的处理速度是非常高的。
5,Disk空间回收
当从MongoDB中删除文档(Documents)或集合(Collections)后,MongoDB不会将Disk空间释放给OS,MongoDB在数据文件(Data Files)中维护Empty Records的列表。当重新插入数据后,MongoDB从Empty Records列表中分配存储空间给新的Document,因此,不需要重新开辟空间。为了更新有效的重用Disk空间,必须重新整理数据碎片。
WiredTiger使用compact 命令,移除集合(Collection)中数据和索引的碎片,并将unused的空间释放,调用语法:
db.runCommand ( { compact: '<collection>' } )
在执行compact命令时,MongoDB会对当前的database加锁,阻塞其他操作。在compact命令执行完成之后,mongod会重建集合的所有索引。
On WiredTiger, compact will rewrite the collection and indexes to minimize disk space by releasing unused disk space to the operating system. This is useful if you have removed a large amount of data from the collection, and do not plan to replace it.
二,In-Memory 存储引擎将数据存储到内存(Memory)
In-Memory存储引擎将数据存储在内存中,除了少量的元数据和诊断(Diagnostic)日志,In-Memory存储引擎不会维护任何存储在硬盘上的数据(On-Disk Data),避免Disk的IO操作,减少数据查询的延迟。
1,指定In-Memory存储引擎
mongod --storageEngine inMemory --dbpath <path>
在选择In-Memory存储引擎时,需要指定两个参数:
- 设置mongod参数: --storageEngine ,设置参数的值是 inMemory;
- 设置mongod参数: --dbpath ,设置参数的值是数据存储的目录;
- 使用Disk存储元数据,诊断数据和临时数据:虽然 In-Memory 存储引擎不会向文件系统写入数据,但是它需要使用 --dbpath 维护少量的元数据和诊断(Diagnostic )日志,在创建Large Index时,使用Disk存储临时数据;Although the in-memory storage engine does not write data to the filesystem, it maintains in the --dbpath small metadata files and diagnostic data as well temporary files for building large indexes.
2,文档级别的并发(document-level concurrency)
In-Memory存储引擎在执行写操作时,使用文件级别的并发控制,就是说,在同一时间,多个写操作能够同时修改同一个集合中的不同文档;当多个写操作修改同一个文档时,必须以序列化方式执行;这意味着,如果该文档正在被修改,其他写操作必须等待。
3,内存使用
In-Mmeory 存储引擎需要将Data,Index,Oplog等存储到内存中,通过mongod参数: --inMemorySizeGB 设置占用的内存数量,默认值是:50% of RAM-1GB。指定In-Memory 存储引擎使用的内存数据量,单位是GB:
mongod --storageEngine inMemory --dbpath <path> --inMemorySizeGB <newSize>
4,持久化(Durable)
由于In-Memory 存储引擎不会持久化存储数据,只将数据存储在内存中,读写操作直接在内存中完成,不会将数据写入到Disk文件中,因此,不需要单独的日志文件,不存在记录日志和等待数据持久化的问题,当MongoDB实例关机或系统异常终止时,所有存储在内存中的数据都将会丢失。
5,记录oplog
In-Memory 存储引擎不会将数据更新写入到Disk,但是会记录oplog,该oplog是存储在内存中的集合,MongoDB通过Replication将Primary成员的oplog推送给同一副本集的其他成员。如果一个MongoDB实例是Replica Set的Primary成员,该实例使用In-Memory存储引擎,通过Replication将oplog推送到其他成员,在其他成员中重做oplog中记录的操作,这样,就能将在Primary成员中执行的数据修改持久化存储。
You can deploy mongod instances that use in-memory storage engine as part of a replica set. For example, as part of a three-member replica set, you could have:
- two mongod instances run with in-memory storage engine.
- one mongod instance run with WiredTiger storage engine. Configure the WiredTiger member as a hidden member (i.e. hidden: true and priority: 0).
With this deployment model, only the mongod instances running with the in-memory storange engine can become the primary. Clients connect only to the in-memory storage engine mongod instances. Even if both mongod instances running in-memory storage engine crash and restart, they can sync from the member running WiredTiger. The hidden mongod instance running with WiredTiger persists the data to disk, including the user data, indexes, and replication configuration information.
三,记录日志
数据是MongoDB的核心,MongoDB必须保证数据的安全,不能丢失,Journal 是顺序写入的日志文件,用于记录上一个Checkpoint之后发生的数据更新,能够将数据库从系统异常终止事件中还原到一个有效的状态。MongoDB使用预写日志机制实现数据的持久化:WiredTiger 存储引擎在执行写操作时,先将数据更新写入到Journal文件。Journal Files是存储在硬盘的日志文件,每个Journal File大约是100MB,存储在--dbpath下的Journal子目录中,在执行Checkpoint操作,将数据的更新同步到数据文件。
每隔一定的时间间隔,WiredTiger 存储引擎都会执行Checkpoint操作,将缓存的数据更新日志同步到硬盘上的数据文件中(On-Disk Files),在默认情况下,MongoDB启用日志记录,也可以显式启用,只需要在启动mongod 时使用--journal 参数:
mongod --journal
1,使用Journal日志文件还原的过程
WiredTiger创建Checkpoint,能够将MongoDB数据库还原到上一个CheckPoint创建时的一致性状态,如果MongoDB在上一个Checkpoint之后异常终止,必须使用Journal日志文件,重做从上一个Checkpoint之后发生的数据更新操作,将数据还原到Journal记录的一致性状态,使用Journal日志还原的过程是:
- 获取上一个Checkpoint创建的标识值:从数据文件(Data Files)中查找上一个Checkpoint发生的标识值(Identifier);
- 根据标识值匹配日志记录:从Journal Files 中搜索日志记录(Record),查找匹配上一个Checkpoint的标识值的日志记录;
- 重做日志记录:重做从上一个Checkpoint之后,记录在Journal Files中的所有日志记录;
2,缓存日志
MongoDB配置WiredTiger使用内存缓冲区来存储Journal Records,所有没有达到128KB的Journal Records都会被缓存在缓冲区中,直到大小超过128KB。在执行写操作时,WiredTiger将Journal Records存储在缓冲区中,如果MongoDB异常关机,存储在内存中的Journal Records将丢失,这意味着,WiredTiger将丢失最大128KB的数据更新。
WiredTiger syncs the buffered journal records to disk according to the following intervals or conditions:
New in version 3.2: Every 50 milliseconds.
MongoDB sets checkpoints to occur in WiredTiger on user data at an interval of 60 seconds or when 2 GB of journal data has been written, whichever occurs first.
If the write operation includes a write concern of j: true, WiredTiger forces a sync of the WiredTiger journal files.
Because MongoDB uses a journal file size limit of 100 MB, WiredTiger creates a new journal file approximately every 100 MB of data. When WiredTiger creates a new journal file, WiredTiger syncs the previous journal file.
3,日志文件(Journal Files)
关于Journal文件,MongoDB在 --dbpath 目录下创建 journal子目录,WiredTiger将Journal 文件存储在该目录下,每一个Journal文件大约是100M,命名格式是:WiredTigerLog.<sequence>,sequence是一个左边填充0的10位数字,从0000000001开始,依次递增。
对于WiredTiger存储引擎,Journal 文件具有以下特性:
- 标识日志记录:Journal文件的每一个日志记录(Record)代表一个写操作;每一个记录都有一个ID,用于唯一标识该记录;
- 压缩Journal文件:WiredTiger会压缩存储在Journal文件中的数据;
- Journal文件大小的上限:每一个Journal文件大小的上限大约是100MB,一旦文件超过该限制,WiredTiger创建一个新的Journal文件;
- 自动移除Journal文件:WiredTiger自动移除老的Journal文件,只维护从上一个Checkpoint还原时必需的Journal文件;
- 预先分配Journal文件:WiredTiger预先分配Journal文件;
4,在异常宕机后恢复数据
在MongoDB实例异常宕机后,重启mongod实例,MongoDB自动重做(redo)所有的Journal Files,在还原Journal Files期间,MongoDB数据库是无法访问的。
四,mongod 跟存储引擎相关的参数
1,使用WiredTiger的参数设置
mongod
--storageEngine wiredTiger
--dbpath <path>
--journal --wiredTigerCacheSizeGB <value>
--wiredTigerJournalCompressor <compressor>
--wiredTigerCollectionBlockCompressor <compressor>
--wiredTigerIndexPrefixCompression <boolean>
2,使用In-Memory的参数设置
mongod
--storageEngine inMemory
--dbpath <path>
--inMemorySizeGB <newSize>
--replSet <setname>
--oplogSize <value>
参考doc: