DBA
分类: Mysql/postgreSQL
2012-11-23 15:29:10
上一篇博客中介绍了Journal的功能和监控等内容,我们可以使用journalLatencyTest命令测试刷新journal到磁盘的时间,使用db.serverStatus()查看当前数据库中journal的性能情况。
当系统启动时,mongodb会将数据文件映射到一块内存区域,称之为Shared view,在不开启journal的系统中,数据直接写入shared view,然后返回,系统每60s刷新这块内存到磁盘,这样,如果断电或down机,就会丢失很多内存中未持久化的数据。当系统开启了journal功能,系统会再映射一块内存区域供journal使用,称之为private view,mongodb默认每100ms刷新privateView到journal,也就是说,断电或宕机,有可能丢失这100ms数据,一般都是可以忍受的,如果不能忍受,那就用程序写log吧。这也是为什么开启journal后mongod使用的虚拟内存是之前的两倍。Mongodb的隔离级别是read_uncommitted,不管使用不使用journal,都是以内存中的数据为准,只不过,不开启journal,数据从shared view读取,开启journal,数据从private view读取。
在开启journal的系统中,写操作从请求到写入磁盘共经历5个步骤,在serverStatus()中已经列出各个步骤消耗的时间。
①、Write to privateView
②、prepLogBuffer
③、WritetoJournal
④、WritetoDataFile
⑤、RemaptoPrivateView
下面详细介绍每个步骤的过程:
流程图:
1、preplogbuffer:
Private view(PV) 中的数据并不是直接刷新到journal文件,而是通过一个中间内存块(journalbuffer,或者alogned buffer)一部分一部分的刷新到journal,这样可以提高并发。preplogbuffer即是将PV中的数据写入到aligned buffer中的过程。这个过程有两部分,basic write 操作和非 basic write操作(e.g.create file)。一次preplogbuffer是以一个commitJob为一个单位,可能会有很多个commitJob写入到aligned buffer,然后提交。一个commitJob中包含多个basic write 和非basic write 操作,basic write是存在Writeintent结构体中的,Writeintent记录了写操作的地址信息。非basic write 操作存在一个vector中。
具体结构如下。
Aligned buffer 有自己的结构,这也是写入到journalfile中的结构。包含Jheader,JsectHeader lsn,Durop,JSectFooter:
每个JsectHeader之间的Durop是属于一个事务范围,一起提交,一起成功,一起失败,即all-or-nothing.上篇文章中介绍的lsn文件,就是记录这个lsn号。
2、WritetoJournal:
writetoJournal操作是将alignedbuffer刷新到JournalFile的过程。默认100ms刷新一次,由--journalCommitInterval 参数控制。writetoJournal会做一些checksum验证,将alignedbuffer进行压缩,然后将压缩过后的alignedbuffer写入到磁盘。写入磁盘后将删除已经满的Journal文件,更新lsn号到lsn文件。写操作到这一步就是安全的了,因为数据已经在磁盘上,如果使用getlasterror(j=true),这一步即可返回。
3、WritetoDataFile:
WritetoDataFile是将未压缩的aligned buffer写入到shared view的过程,然后由操作系统刷新到磁盘文件中。WritetoDataFile首先会对aligned buffer进行严格的验证,确保没有改变过,然后解析aligned buffer,通过memcpy函数拷贝到shareview
4、RemaptoprivateView:
RemaptoprivateView会将持久化的数据重新映射到PV,以减小PV的大小,防止它不断扩大,按照源码上说,RemaptoprivateView会两秒钟重新映射一次,大约有1000个view,不是一次全做完,而是一部分一部分的做。由于读操作是读取PV,所以在映射完成之后会有短暂的时间读取磁盘。
经过这四步,一个写操作就完成了,journal提高了数据的安全性,并不像想象中的会丢数据,重要的是如何使用和维护。
以上均参考自mongo官方文档和源码,有理解不对的地方也请大家指正。
参考:
1、
2、
3、
4、源码/src/mongo/db/dur.*
horizonhyg2014-01-16 11:36:01
wei-xh:你好,咨询个问题
db.runCommand("journalLatencyTest")
{
"timeMillis" : {
"8KB" : 7.19,
"8KBWithPauses" : 4.53102,
"1MB" : 8.15
&nb
恩?看不到全部内容。。。
回复 | 举报horizonhyg2013-06-18 21:14:55
很高兴一起讨论,你分析的很对,PV确实是从SV remap的,使用mongoMMF,memory mapped file,是全部重新映射,一般要几ms。至于pv的增大,是这个样子,因为pv是写时复制,映射并不占用多少内存,只有当数据发生改变的时候,才会开辟真正的内存空间,记录这些变化,真正增大的正是这些修改占用的内存空间,remap正是要释放这些空间。
remap是要在写锁下进行的,所以,不允许写操作。
更详细的可以参见:http://blog.csdn.net/yhjj0108/article/details/8284282