一、什么是MongoDB
1.1 基本介绍
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,可以添加更多的节点,来保证服务器性能。MongoDB 将数据存储为一个文档,数据结构由键值对组成。MongoDB 文档使用BSON格式;字段值可以包含其他文档,数组及文档数组。
1.2 BSON
BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。
BSON可以做为网络数据交换的一种存储形式,这个有点类似于Google的Protocol Buffer,但是BSON是一种schema-less的存储形式,它的优点是灵活性高,但它的缺点是空间利用率不是很理想。
BSON有三个特点:轻量性、可遍历性、高效性。
MongoDB使用了BSON这种结构来存储数据和网络数据交换。把这种格式转化成一文档这个概念,因为BSON是schema-free的,所以在MongoDB中所对应的文档也有这个特征,这里的一个文档也可以理解成关系数据库中的一条记录,只是这里的文档的变化更丰富一些,如文档可以嵌套。
MongoDB以BSON做为其存储结构的一种重要原因是其可遍历性。
1.3 MongoDB关键特性
1、高性能
2、丰富的查询语言
3、高可用性
副本集,自动故障转移,数据冗余
4、水平可扩展性
5、支持多种存储引擎
MMAPv1存储引擎:<3.2的默认存储引擎
WirdeTiger存储引擎:>=3.2的默认存储引擎
In-Memory存储引擎:Changed in version 3.2.6
1.4 默认库
MongoDB有三个默认库:admin、local、config。
admin:
从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
local:
这个库的数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
config:
当MongoDB用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
二、MongoDB的存储引擎
2.1 WirdeTiger
2.1.1 基本介绍
3.2之后的默认存储引擎,用于将数据持久化存储到硬盘文件中,WiredTiger提供文档级别的并发控制,检查点,数据压缩和本地数据加密等功能。对于生产环境,更多的CPU可以有效提升wireTiger的性能,因为它是多线程的。wiredTiger不像MMAPV1引擎那样尽可能的耗尽内存,它可以通过在配置文件中指定“cacheSizeGB”参数设定引擎使用的内存量,此内存用于缓存工作集数据(索引、namespace,未提交的write,query缓冲等)。
2.1.2 文档级别的并发控制
MongoDB在执行写操作时,WiredTiger 在文档级别进行并发控制,就是说,在同一时间,多个写操作能够修改同一个集合中的不同文档;当多个写操作修改同一个文档时,必须以序列化方式执行;这意味着,如果该文档正在被修改,其他写操作必须等待,直到在该文档上的写操作完成之后,其他写操作相互竞争,获胜的写操作在该文档上执行修改操作。
对于大多数读写操作,WiredTiger使用乐观并发控制,只在Global,database和Collection级别上使用意向锁,如果WiredTiger检测到两个操作发生冲突时,导致MongoDB将其中一个操作重新执行,这个过程是系统自动完成的。
2.1.3 检查点
在检查点操作开始时,WiredTiger提供指定时间点的数据库快照,该快照呈现的是内存中数据的一致性视图。当向磁盘写入数据时,WiredTiger将快照中的所有数据以一致性方式写入到数据文件中。一旦检查点创建成功,WiredTiger保证数据文件和内存数据是一致性的,因此,检查点担当的是还原点,检查点操作能够缩短MongoDB从Journal日志文件还原数据的时间。
当WiredTiger创建检查点时,MongoDB将数据刷新到数据文件中,在默认情况下,WiredTiger创建检查点的时间间隔是60s,或产生2GB的Journal文件。在WiredTiger创建新的检查点期间,上一个检查点仍然是有效的,这意味着,即使MongoDB在创建新的检查点期间遭遇到错误而异常终止运行,只要重启,MongoDB就能从上一个有效的检查点开始还原数据。
当MongoDB以原子方式更新WiredTiger的元数据表,使其引用新的检查点时,表明新的检查点创建成功,MongoDB将老的检查点占用的磁盘空间释放。使用WiredTiger 存储引擎,如果没有记录数据更新的日志,MongoDB只能还原到上一个检查点;如果要还原在上一个检查点之后执行的修改操作,必须使用Jounal日志文件。
2.1.4 预写记录日志
WiredTiger使用预写日志的机制,在数据更新时,先将数据更新写入到日志文件,然后在创建检查点操作开始时,将日志文件中记录的操作,刷新到数据文件,就是说,通过预写日志和检查点,将数据更新持久化到数据文件中,实现数据的一致性。WiredTiger 日志文件会持久化记录从上一次检查点操作之后发生的所有数据更新,在MongoDB系统崩溃时,通过日志文件能够还原从上次检查点操作之后发生的数据更新。
2.1.5 内存使用
WiredTiger 利用系统内存资源缓存两部分数据:
内部缓存(Internal Cache)
文件系统缓存(Filesystem Cache)
从MongoDB3.2版本开始,WiredTiger内部缓存的使用量,默认值是:1GB 或 60% of RAM - 1GB,取两值中的较大值;文件系统缓存的使用量不固定,MongoDB自动使用系统空闲的内存,这些内存不被WiredTiger缓存和其他进程使用,数据在文件系统缓存中是压缩存储的。
WiredTiger压缩存储集合和索引,压缩减少磁盘空间消耗,但是消耗额外的CPU执行数据压缩和解压缩的操作。
默认情况下,WiredTiger使用块压缩算法来压缩Collections,使用前缀压缩算法来压缩Indexes,Journal日志文件也是压缩存储的。对于大多数工作负载,默认的压缩设置能够均衡数据存储的效率和处理数据的需求,即压缩和解压的处理速度是非常高的。
当从MongoDB中删除文档或集合后,MongoDB不会将磁盘空间释放给OS,MongoDB在数据文件中维护Empty Records的列表。当重新插入数据后,MongoDB从Empty Records列表中分配存储空间给新的文档,因此,不需要重新开辟空间。为了更新有效的重用磁盘空间,必须重新整理数据碎片。
WiredTiger使用compact 命令,移除集合中数据和索引的碎片,并将unused的空间释放,调用语法:
db.runCommand ( { compact: '<collection>' } )
在执行compact命令时,MongoDB会对当前的database加锁,阻塞其他操作。在compact命令执行完成之后,mongod会重建集合的所有索引。
2.2 In-Memory
2.2.1 基本介绍
In-Memory存储引擎将数据存储在内存中,除了少量的元数据和诊断日志,In-Memory存储引擎不会维护任何存储在硬盘上的数据,避免磁盘的IO操作,减少数据查询的延迟。
2.2.2 文档级别的并发
In-Memory存储引擎在执行写操作时,使用文件级别的并发控制,就是说,在同一时间,多个写操作能够同时修改同一个集合中的不同文档;当多个写操作修改同一个文档时,必须以序列化方式执行;这意味着,如果该文档正在被修改,其他写操作必须等待。
2.2.3 内存使用
In-Mmeory 存储引擎需要将Data,Index,Oplog等存储到内存中,通过mongod参数: --inMemorySizeGB 设置占用的内存数量,默认值是:50% of RAM-1GB。指定In-Memory 存储引擎使用的内存数据量,单位是GB:
mongod --storageEngine inMemory --dbpath <path> --inMemorySizeGB <newSize>
2.2.4 持久化
由于In-Memory 存储引擎不会持久化存储数据,只将数据存储在内存中,读写操作直接在内存中完成,不会将数据写入到磁盘文件中,因此,不需要单独的日志文件,不存在记录日志和等待数据持久化的问题,当MongoDB实例关机或系统异常终止时,所有存储在内存中的数据都将会丢失。
2.2.5 oplog
In-Memory 存储引擎不会将数据更新写入到磁盘,但是会记录oplog,该oplog是存储在内存中的集合,MongoDB通过Replication将Primary成员的oplog推送给同一副本集的其他成员。如果一个MongoDB实例是Replica Set的Primary成员,该实例使用In-Memory存储引擎,通过Replication将oplog推送到其他成员,在其他成员中重做oplog中记录的操作,这样,就能将在Primary成员中执行的数据修改持久化存储。
三、MongoDB的日志
数据是MongoDB的核心,MongoDB必须保证数据的安全,不能丢失,Journal 是顺序写入的日志文件,用于记录上一个检查点之后发生的数据更新,能够将数据库从系统异常终止事件中还原到一个有效的状态。MongoDB使用预写日志机制实现数据的持久化:WiredTiger 存储引擎在执行写操作时,先将数据更新写入到Journal文件。Journal Files是存储在硬盘的日志文件,每个Journal File大约是100MB,存储在--dbpath下的Journal子目录中,在执行检查点操作,将数据的更新同步到数据文件。
每隔一定的时间间隔,WiredTiger 存储引擎都会执行检查点操作,将缓存的数据更新日志同步到硬盘上的数据文件中(On-磁盘 Files),在默认情况下,MongoDB启用日志记录,也可以显式启用,只需要在启动mongod 时使用--journal 参数:
mongod --journal
3.1 使用Journal日志文件还原的过程
WiredTiger创建检查点,能够将MongoDB数据库还原到上一个CheckPoint创建时的一致性状态,如果MongoDB在上一个检查点之后异常终止,必须使用Journal日志文件,重做从上一个检查点之后发生的数据更新操作,将数据还原到Journal记录的一致性状态,使用Journal日志还原的过程是:
获取上一个检查点创建的标识值:从数据文件(Data Files)中查找上一个检查点发生的标识值(Identifier);
根据标识值匹配日志记录:从Journal Files 中搜索日志记录(Record),查找匹配上一个检查点的标识值的日志记录;
重做日志记录:重做从上一个检查点之后,记录在Journal Files中的所有日志记录;
3.2 缓存日志
MongoDB配置WiredTiger使用内存缓冲区来存储Journal Records,所有没有达到128KB的Journal Records都会被缓存在缓冲区中,直到大小超过128KB。在执行写操作时,WiredTiger将Journal Records存储在缓冲区中,如果MongoDB异常关机,存储在内存中的Journal Records将丢失,这意味着,WiredTiger将丢失最大128KB的数据更新。
3.3 日志文件
关于Journal文件,MongoDB在 --dbpath 目录下创建 journal子目录,WiredTiger将Journal 文件存储在该目录下,每一个Journal文件大约是100M,命名格式是:WiredTigerLog.,sequence是一个左边填充0的10位数字,从0000000001开始,依次递增。
对于WiredTiger存储引擎,Journal 文件具有以下特性:
标识日志记录:Journal文件的每一个日志记录(Record)代表一个写操作;每一个记录都有一个ID,用于唯一标识该记录;
压缩Journal文件:WiredTiger会压缩存储在Journal文件中的数据;
Journal文件大小的上限:每一个Journal文件大小的上限大约是100MB,一旦文件超过该限制,WiredTiger创建一个新的Journal文件;
自动移除Journal文件:WiredTiger自动移除老的Journal文件,只维护从上一个检查点还原时必需的Journal文件;
预先分配Journal文件:WiredTiger预先分配Journal文件;
3.4 在异常宕机后恢复数据
在MongoDB实例异常宕机后,重启mongod实例,MongoDB自动重做(redo)所有的Journal Files,在还原Journal Files期间,MongoDB数据库是无法访问的。
四、MongoDB相关参数
4.1 WiredTiger参数设置
mongod
--storageEngine wiredTiger
--dbpath <path>
--journal --wiredTigerCacheSizeGB <value>
--wiredTigerJournalCompressor <compressor>
--wiredTigerCollectionBlockCompressor <compressor>
--wiredTigerIndexPrefixCompression <boolean>
4.2 In-Memory参数设置
mongod
--storageEngine inMemory
--dbpath <path>
--inMemorySizeGB <newSize>
--replSet <setname>
--oplogSize <value>
jurnal就是一个预写事务日志,来确保数据的持久性,wiredTiger每隔60秒(默认)或者待写入的数据达到2G时,MongoDB将对journal文件提交一个checkpoint(检测点,将内存中的数据变更flush到磁盘中的数据文件中,并做一个标记点,表示此前的数据表示已经持久存储在了数据文件中,此后的数据变更存在于内存和journal日志)。对于write操作,首先被持久写入journal,然后在内存中保存变更数据,条件满足后提交一个新的检测点,即检测点之前的数据只是在journal中持久存储,但并没有在MongoDB的数据文件中持久化,延迟持久化可以提升磁盘效率,如果在提交checkpoint之前,MongoDB异常退出,此后再次启动可以根据journal日志恢复数据。journal日志默认每个100毫秒同步磁盘一次,每100M数据生成一个新的journal文件,journal默认使用了snappy压缩,检测点创建后,此前的journal日志即可清除。mongod可以禁用journal,这在一定程度上可以降低它带来的开支;对于单点mongod,关闭journal可能会在异常关闭时丢失checkpoint之间的数据(那些尚未提交到磁盘数据文件的数据);对于replica set架构,持久性的保证稍高,但仍然不能保证绝对的安全(比如replica set中所有节点几乎同时退出时)。
五、总结
本文介绍了MongoDB的一些基本特性、存储引擎以及日志相关的内容;在以后的文章中会重点分析对MongoDB的特性进行分析。