Mongodb Journaling 机制

当MongoDB运行在journal开启的状态下, 写操作 会在写入磁盘数据文件之前先写入内存和journal文件。本文讨论MongoDB 系统中journaling 的实现和技术细节。更多关于配置、调试、管理journaling的信息见管理Journaling 

Journal 文件

Journaling开启后, MongoDB会在定义好的dbPath 路径下创建一个 journal子目录,dbpath路径默认为/data/db。这个目录用来存放journal文件,文件中记录的是write-ahead日志。

(译者注:即WAL,预写式日志。数据库系统在运行时将对数据库的修改写到redo日志中而不是数据文件,然后redo日志通过checkpointgroup commit刷到最终的数据文件里。这是大多数数据库系统采用的做法)

目录下还有一个文件来保存某个最近的序列号。

(译者注:这个文件是journal目录下的lsn文件,里边保存的序列号并非是journal文件的序列号。这个就说来话长了,上边的注释提到写操作在写入数据文件之前会写到journal文件中,而在写入journal文件之前会先写入到一块内存区域,这个内存区域叫private view,官方表示说private view的数据默认每100ms刷到journal文件中,但其实是从private view分批刷到某个临时内存区域,然后从临时内存区域再刷到journal文件,这个刷到临时内存的批次就是lsn文件中保存的序列号)

一次正常的退出会删除journal目录下所有文件,而一次非正常退出(比如崩溃)则不会;重启mongod进程时会根据journal下的文件来恢复数据以达到数据一致。

Journal文件是append-only文件,文件名以j._为前辍。当超过1G时,MongoDB会创建一个新的文件。一旦某个journal文件的数据全部刷到数据文件之后,MongoDB会删掉这个journal文件,因为它起不到恢复系统的作用了。除非你的系统每秒写入大量数据,否则通常情况下journal目录应该只有两三个文件。

如果你愿意的话,可以在启动mongod 进程时设置storage.smallFiles 属性,它会将journal文件大小限制到128M

如果要提高journal文件非常频繁的顺序写入性能,你可以将journal目录放置在和数据文件不同的文件系统下。

重要:
如果将journal和数据文件放置在不同的文件系统中,将不能使用单独使用文件系统快照来备份
dbPath 目录下的文件。在这种场景下,先使用fsyncLock()来确保数据文件一致性,等快照生成完毕之后使用fsyncUnlock() 来释放锁定。

注意:
根据你的文件系统的不同,开启journaling第一次启动
mongod进程时系统可能会有点滞后,因为要为journal文件预分配空间。如果
mongod 进程认为预分配journal文件比在需要时再去创建文件更高效,MongoDB会事先将它分配好。预分配文件的时间可能会持续几分钟,在这段时间内是连接不了数据库的。这是一次性的行为,在以后的调用中不会发生。


如果要避免预分配带来的滞后,参见
避免预分配滞后

Journaling中的存储视图

Journaling中有3个内部存储视图来服务于MongoDB

shared view存储修改后的数据然后刷到磁盘的数据文件中。shared view是唯一一个能够直接访问数据文件的视图。当运行时journaling开启,mongod 进程会让操作系统将磁盘上所有的数据文件映射到shared view虚拟内存。操作系统只映射文件不会加载真实数据。只有在需要时才会将数据加载到shared view中。

private view 中的数据用来响应读操作private view是一个接收MongoDB写操作的地方。当journal提交后,MongoDBprivate view的更改复制到shared view, 这些更改最终通过shared view刷到磁盘数据文件中。

Journal文件是一个基于磁盘的视图,在数据改动刷到磁盘数据文件之前它用来存储private view 接收到的写操作。Journal为数据库提供了健壮性,如果 mongod进程在将数据写入到磁盘数据文件之前崩溃了,下次启动时会回放 journal中的写操作到shared view,并最终会将改动刷到磁盘数据文件中。

译者注:官方文档对视图的描述比较简单,这里我对整个持久化流图做了一下梳理,也是对文章开头我画的图的解释。

1)   Mongodb启动时将数据文件映射到shared view,这是内存映射,并非加载所有数据

2)   Shared view映射到private view

3)   Private view通过group commit将客户端的写操作写入到journal文件,其实这中间还经历了一次aligned buffer,官方所说的group commit其实是从aligned bufferjournal文件

4)   Jaournal文件将写操作回放给shared view

5)   Shared view 将数据改动刷到磁盘数据文件

6)   Shared view重新映射到private view

Journaling如何记录写操作

MongoDB将写操作按批次复制到journal文件, 这种方式称之为批量提交。 将数据按照批量提交” 可以有效的减少journaling机制的性能开销,因为每次提交发生时都会阻塞住所有写操作。批量提交的默认时间间隔见commitIntervalMs 参数。

Journaling存储的都是原生操作,MongoDB能够利用它们来重放以下操作:

·         文档的插入/更新

·         索引的修改

·         对命名空间元数据进行修改

·         创建和删除数据库,以及一些相关数据文件

当一个写操发生时,MongoDB将数据写入内存中的private view,然后从private view将写操作分批复制到到journal文件。Journal文件存储在磁盘上以保证健壮性。每条journal数据都描述了写操作让数据文件发生变化的具体地址。

接下来MongoDBjournal中的写操作提交到shared view中,这时shared view和数据文件中的数据会不一致。

默认情况下每隔60秒,MongoDB通过操作系统将shared view的数据改动刷到磁盘上,这样使得最新的写操作能体现在数据文件中。有时操作系统刷磁盘的间隔会超过60秒,特别是在系统空闲内存比较少的时候。

MongoDB将数据往磁盘上刷时,会记住这些刷过的数据。一旦某个journal文件上记录的所有操作都被刷到数据文件中后,这个文件就再也起不到恢复数据的作用了,MongoDB会删除这个文件,也可能回收它用作一个新的journal文件。

做为整个journaling机制的一部分, MongoDB会照常请求操作系统将shared view重新映射到private view,这样是为了节约物理内存。基于一次新的重映射,操作系统会将物理内存页共享给shared viewprivate view

(译者注:shared view初始映射到private view时,private view是只读的,当写操作进来时,mongodbprivate view中映射数据的所在页变更为可写,然后从数据文件复制一份真实数据的拷贝,并将写操作的数据写入进来。 只有在这时private view才会单独的去消耗内存,初始映射的时候是没有内存消耗的,所以官方说初始时是共享物理内存页)

注意:
shared view
和磁盘数据文件之间的这部分交互和不使用journaling时大致是一样的同样都是MongoDB每隔60秒请求操作系统将内存中的数据改动刷到数据文件中。


--本篇文章转自: Journaling 机制

你可能感兴趣的:(Mongodb Journaling 机制)