Chinaunix首页 | 论坛 | 博客
  • 博客访问: 808917
  • 博文数量: 50
  • 博客积分: 757
  • 博客等级: 上士
  • 技术积分: 1913
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-29 14:29
个人简介

DBA

文章分类

全部博文(50)

文章存档

2015年(3)

2014年(2)

2013年(14)

2012年(19)

2011年(12)

分类: 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

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中。

具体结构如下。

图片2

Aligned buffer 有自己的结构,这也是写入到journalfile中的结构。包含Jheader,JsectHeader lsn,Durop,JSectFooter:

3

     每个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.*

阅读(13145) | 评论(5) | 转发(0) |
给主人留下些什么吧!~~

lidakun2014-07-20 11:42:41

你好,我这里有一个很大的疑问,请解惑
一,文章中的写时复制,是什么意思?是不是在数据修改的时候,mongodb会把被修改的数据从shared view复制到privite view,然后修改privte view中的数据?
二,private view是一个映射区域,他真正占用内存的是类似于mysql中的redo?还是从shared view复制过来的数据?

horizonhyg2014-01-16 11:36:01

wei-xh:你好,咨询个问题



db.runCommand("journalLatencyTest")
{
        "timeMillis" : {
                "8KB" : 7.19,
                "8KBWithPauses" : 4.53102,
                "1MB" : 8.15
&nb

恩?看不到全部内容。。。

回复 | 举报

wei-xh2014-01-15 11:04:34

你好,咨询个问题



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