16.Mongodb之预写日志(Journaling)

https://docs.mongodb.com/manual/core/journaling/

https://docs.mongodb.com/manual/core/wiredtiger/#std-label-storage-wiredtiger-checkpoints

前言:本文所说的日志是WiredTiger预写日志(WiredTiger write-ahead log)即Journal,而不是Mongodb日志文件。

检查点(checkpoint):其实就是60s(默认)间隔的数据快照。这个快照显示内存中数据的一致视图。写入磁盘时WT将快照中的所有数据以一致的方式写入磁盘,这个持久化的数据就充当了数据文件中的检查点。这个检查点可以用作恢复点。WT使用检查点提供磁盘数据的一致视图,并且允许mongodb从上一个检查点恢复。但是如果mongodb在两个检查点之间以外退出,则需要journaling来恢复上次检查点之后的操作情况。

关于压缩:

        预写日志使用 snappy 压缩库。如果要明确压缩算法或者不压缩,可以通过 storage.wiredTiger.engineConfig.journalCompressor 来设置。关于journal的压缩更多可以参照 这里

        MongoDB默认对整个文档都会压缩,在创建集合的时候可以指定压缩算法。这个压缩是针对 Page 级别的。在内存 Page 写入磁盘前,会通过 snappy/zlib/zstd 等压缩算法对整个 page 的数据进行压缩之后写入磁盘。如果要对单个字段进行压缩的话,建议在 Client 端去做。我们有些业务就是在 Client 端对某个字段进行压缩加密等操作之后再写入 Server 端。

注:也就是说默认其实都是有压缩的了,这个可能不需要使用方来关注。这里

WiredTiger利用预写日志(Journaling)+检查点(checkpoint)实现数据持久性存储!!

1、两个重要的存储视图

        Journaling功能用到了两个重要的内存视图:共享视图(shared view)和私有视图(private view)。这两个内存视图都是通过MMAP(内存映射)来实现的。①private view映射内存的修改不会(直接)影响磁盘;private view是为读操作保存数据的位置,同时也是mongodb保存新的写操作的第一个地方。②shared view中的数据变化会影响到磁盘上的文件,系统会周期性的刷新shared view中的数据到磁盘。

        启动mongod的时候,数据文件会被映射到共享视图(shared view)。此时操作系统只是完成映射,并不会立即加载数据到内存,mongdb会根据需要加载数据到shared view(按需加载)。

注:关于上述MMAP可能有人会有疑问,磁盘那么大内存那么小怎么可能都映射过来?这里之说一点就好了:映射到的是虚拟内存,而不是物理内存。当真正需要访问某数据的时候才通过触发“缺页中断”的方式把实际数据加载到物理内存;物理内存实际映射的虚拟页也是会不断轮替的。

2、工作原理

(1)前面说了当mongod进程启动后首先会将数据文件映射到shared视图中。如下图所示。

  

(2)写操作或修改发生。进程会修改内存中的数据,此时磁盘上的文件数据就与内存中的数据不一致了。此时根据Journaling功能是否打开分为两种情况。

        1)没打开。操作系统每60秒将内存中的变化刷新到磁盘。显然这个不是我们讨论的重点。

        2)启用journaling的时候,mongod会再做一次映射,映射出一个私有视图(private view),如图2所示。值得注意的是私有视图没有映射到数据文件,所以操作系统不能直接的将private视图中的任何改变刷新到磁盘。

        当写操作发生时,mongdb首先将数据写到内存中的private视图处,如图3所示。然后mongdb将写操作批量复制到journal,journal会将写操作存储到磁盘文件上使其持久化保存(默认间隔100ms)。journal日志文件上的每一个条目都描述了写操作更改了数据文件的那些字节,如图4所示。关于这一步mongdb采用的group commit是的方式。

         Mongdb采用group commits的方式将写操作批量复制到journal日志文件中。group commit提交方式能够最小化journal日志机制对性能的影响(意思是说:如果每次操作都同步Journal的话对性能影响就太大了,这个是不可接受的)。因此group commit方式在提交过程中必须阻塞所有写入操作。

        由于数据文件的变化被持久化到了journal日志文件中,即便此时mongodb服务器意外崩溃,写操作也是安全的。因为当数据库重新启动的时候会去读journal日志文件,并将写操作引起的变化重新同步到数据文件中去。

        上面步骤完成后,mongodb会利用journal日志中的写操作记录引起的数据变化来更新shared视图中的数据(执行journal的写操作到shared视图),如图6。当所有的变化操作都更新到shared视图后,mongodb将重新利用shared视图来映射出一个全新的private视图,防止private视图变得“太脏”,这个时候其内存空间恢复到初始值,约为0(这里应该也是写时复制copy-on-write的意思,当被修改的时候才回去真正的同shared view分开)。

        此时shared视图内存中的数据与磁盘上的数据变得不一致了。mongodb会周期性的要求操作系统将shard视图中变化的数据刷新(flush)到磁盘上,使磁盘上的数据与内存中的数据保持一致,如图7。如果系统内存资源不足的时候,操作系统就会选择以更高的频率刷入shared视图到磁盘。

        当执行完刷新内存中变化的数据到磁盘后,mongdb会删除点journal中的这个时间点之后的所有写操作,这一点与关系型数据中的checkpoint类似。

         mongodb的Journaling日志功能是默认开启的。上面的过程涉及两个周期。一个是将写操作周期性同步到journal日志文件的周期,这个周期大小通过journal Commitlnterval来控制,默认值是100ms。另一个是mongdb经过60s的周期刷新内存中的变化数据到磁盘,这个值是通过可选参数syncdely来控制的。这些默认值一般适应于大多数情况,不要轻易更改。其实通过上面分析我们知道数据库服务器仍然有100ms时段的数据丢失风险,因为Journaling日志写到磁盘的周期是100ms,倘若刚好一批写操作还在内存没等到下一个100ms同步到journal磁盘文件服务器就故障了,这些还在内存中的数据就会丢失。

3、journal日志带来的问题

影响一:对mongdb写入性能的影响

        由于提交journal日志会产生写入阻塞,所以他会对写入操作的性能产生影响;不过好在对读操作不会有影响。另外在生产环境开启Journaling是很有必要的。

影响二:对磁盘空间的影响

       Journal就是一个磁盘上的持久化文件,显然也会产生磁盘容量的消耗。这也是mongodb吃磁盘容量的原因之一。

        开启journal日志功能,mongodb会在数据目录下创建一个journal文件夹,用来存放预写重放日志。如果mongdb安全关闭,会自动删除目录下的文件;如果崩溃导致的关闭则不会删除以供重启时自动修复数据。journal日志文件是一个不断往文件尾追加内容的文件,他的命令是j._开头后面接一个数字作为序列号。如果文件超过1G,mongodb会新建journal文件。journal日志文件一般情况只会生成两三个,除非有大量数据插入。对于已经flush到磁盘的数据文件,mongodb会删除或这种利用,总之不会保留因为这些数据已经不会被用来数据恢复了。

总结:其实这样就说通了。每60秒间隔数据就会刷新到磁盘中,这个被称为检查点,这些数据已经持久化存储没什么可担心的。此外还有更细粒度的保障那就是100ms周期的预写入日志Journaling。每100ms修改就会持久化到journal中,显然持久化进去后数据也是安全的。唯一不安全的就是两次批量提交journal的这100ms间隔的修改。这个没办法解决,group commit降低journal对性能的影响的必要手段,当然你可以通过调整这个Commitlnterval来实现权衡,但100ms已经是相对最优的间隔了。

问题一:WT写入磁盘的频率如何?

        显然默认60s。

问题二:实际验证下mongdb的数据文件和journal文件是如何组织的?

(1)通过进程我们可以知道数据存放路径为 /var/lib/mongo 

(2)进入路径发现每个集合对应一个文件、每个索引也对应一个文件,另外还有预写日志journal。此时文件数为50;接下来我们新建一个集合并写入一条数据看看有什么变化。

 (3)db.coll.insert({name:"shuozhuoshuozhuo",age:25}),

问题三:关于mongodb cache的使用?

         关于mongodb内存的占用,其实包括两块(或者说叫两级缓存)。一级是WT cache(WiredTiger内部缓存),另一级是filesystem cache(文件系统缓存或者fs.cache)。其中前者的占用是可调的,后者具体占用多少物理内存应该就是操作系统的事情了。对于Linux系统来说其默认设置倾向于把内存尽可能的用于文件cache。

 

关于文件系统缓存:

        操作系统为了提高IO的性能而引入了文件系统缓存(File System Cache),系统会将一批IO操作的修改暂存到缓存,周期性的提交给磁盘。

        cache的规则很简单。我们肯定希望cache能够尽可能满足正在执行的工作,从应用程序栈的角度考虑越往下层cache越不那么高效。所以,如果你的应用程序有能力去cache,最好就把内存更多的留给应用程序而不是文件系统。不过文件系统缓存在一些场合仍然是有用的,比如写日志。如果让你做一个选择是给数据库10G内存还是给文件系统cache10G内存,显然应该把内存给数据库。所以高效的磁盘数据库往往自己提供了缓存,而不是依赖于文件系统缓存。从理论上讲,Mysql Innodb实现了自己缓存的数据库会更智能,应该比Mongodb这样依赖于文件系统来刷新数据的数据库要来的高效。

这个文件系统指的就是VFS(Virtual File System)了,和kafka使用的PageCache是一个东西。

https://docs.mongodb.com/manual/faq/storage/

https://docs.mongoing.com/faq/mongodb-storage

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

焱齿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值