MongoDB源码概述——内存管理和存储引擎

原文地址:http://creator.cnblogs.com/

数据存储:

  之前在介绍Journal的时候有说到为什么MongoDB会先把数据放入内存,而不是直接持久化到数据库存储文件,这与MongoDB对数据库记录文件的存储管理操作有关。MongoDB采用操作系统底层提供的内存文件映射(MMap)的方式来实现对数据库记录文件的访问,MMAP可以把磁盘文件的全部内容直接映射到进程的内存空间,这样文件中的每条数据记录就会在内存中有对应的地址,这时对文件的读写可以直接通过操作内存来完成(而不是fread,fwrite之辈).

  这里顺便提一句,MMAP的只是将文件映射到进程空间,而不是直接全部map到物理内存,只有访问到这块数据时才会被操作系统以Page的方式换到物理内存。这部分的管理工作由操作系统完成,对于MongoDB的开发者而言,也是透明的.其实我们所能用的所有函数,包括系统内核里的实现函数,操作的统统都是虚拟内存,也就是每个进程所谓的4GB(32位系统)的虚拟地址空间.物理内存对于用户是不可见的,不可操作的。这也就是为什么MongoDB可以存储比内存更大的数据,但是却不建议热数据超过内存大小的原因。因为热数据大于内存的话,操作系统需要频繁的换入换出物理内存中的数据,会严重影响MongoDB的性能。

clip_image001

32位操作系统进程虚拟内存表图:

clip_image002  使用这种内存管理方式极大的减轻了MongoDB开发者的负担,把大量的内存管理的工作交由操作系统来完成,在写这篇文章的时候我自个儿我总结了下她的特点,可是后面发现有本书上有总结,于是直接贴上来(加了几个下划线),没办法,人家比我总结得好。

• MongoDB’s code for managing memory is small and clean, because most of that work is pushed to the operating system.

• The virtual size of a MongoDB server process is often very large, exceeding the size of the entire data set. This is OK, because the operating system will handle keeping the amount of data resident in memory contained.

• MongoDB cannot control the order that data is written to disk, which makes it

impossible to use a writeahead log to provide single-server durability. Work is

ongoing on an alternative storage engine for MongoDB to provide single-server

durability.

• 32-bit MongoDB servers are limited to a total of about 2GB of data per mongod.

This is because all of the data must be addressable using only 32 bits.

(如果你想了解更多MMAP相关的东东,可以翻阅《Unix网络编程 卷二》的12.2节)

 

  好了,抽象的东西讲述完毕,下面来点硬货!!!

存储源码分析:

  在MongoMMF类的定义(momgommf.h 29)中需要注意一下几个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
void* map(const char *filename, unsigned long long &length, int options = 0 );
 
//将文件filename以MMAP的方式映射到进程的空间(称之为视图),返回在内存中的首地址
 
//如果文件不存在,会通过mmap_win里的CreateFile创建文件
 
void flush(bool sync);
 
//将映射到进程空间的数据Flush到磁盘
 
void* getView() const
 
//获取视图首地址

  关于这三个方法的内部实现,自然我们可以想到是对操作系统的API的调用,对于不同的操作系统,方法签名以及参数还有变化,在这里我就不罗嗦了,各个系统的API都查得到。所以我们这里也并不会贴出其内部调用的系统API.

  究竟MongoDB是什么时候map数据库文件到内存的呢?又是何时将内存中映射的数据flush到磁盘进行持久化的呢?下面我们来分析一下这两个问题。

 

map数据库文件到内存:

  在我们第一次向一个未创建的数据库插入一条记录时,调用的函数会由如下流程:

1
DataFileMgr::insert()——》Database::allocExtent()——》Database::suitableFile()——》 Database::getFile()——》MongoDataFile::open()——》 MongoMMF::create()

  DataFileMgr::insert()之前有些方法我已经省略了,这个调用流程比较长,但是最终会调用到MongoMMF::create()来创建第一个数据库文件

1
2
3
4
5
6
bool MongoMMF::create(string fname, unsigned long long& len, bool sequentialHint) {
        setPath(fname);
        _view_write = map(fname.c_str(), len, sequentialHint ? SEQUENTIAL : 0);
        //如果文件不存在,会通过mmap_win里的CreateFile创建文件,MemoryMappedFile::map方法
        return finishOpening();
    }

  观察代码后我们发现create方法直接调用了map,而map的内部,就有文件创建功能,创建完后就map到内存了。

  若是向现有数据库插入记录,则在Database构造的期间会调用openAllFiles(),进入上面流程的Database::getFile()部分

  终上所述两种情况,我们明白了MongoDB何时将数据库记录文件map到内存.

Flush数据进行持久化:

  MongoDB中默认每分钟Flush一次进行持久化存储,当然这个间歇可以通过"--syncdelay"启动参数来进行设置.执行流程为main()——》dataFileSync.go()。DataFileSync派生自BackgroundJob,其go()方法会创建一个新的线程来运行虚函数run()。

  Run()最后调用MemoryMappedFile::flushAll方法对所有的映射文件进行flush操作,将更改持久化到磁盘.前面在介绍MongoMMF的时候就介绍过此方法.这里不再累述。

  这里顺便提一句,其实mmap不调用fsync强刷到磁盘,操作系统也是会帮我们自动刷到磁盘的,linux有个dirty_writeback_centisecs参数用于定义脏数据在内存停留的时间(默认为500,即5秒),过了这个timeout时间就会被系统刷到磁盘上。在这个自动刷的过程中是会阻塞所有的IO操作的,如果要刷的数据特别多的话,容易产生一些长耗时的操作,例如有些使用mmap的程序每隔一段时间就会出现有超时操作,一般的优化手段是考虑修改系统参数dirty_writeback_centisecs,加快脏页刷写频率来减少长耗时。mongodb是定时强刷,不会有此问题。

问题的出现:

  弄清楚了MongoDB的存储引擎何时将数据库记录文件map到进程的内存空间以及何时flush到原文件时,不知道您发现了问题没有?持久化的flush过程是每分钟调用一次,而写数据是时时刻刻进行的,若还没有到一分钟,在59秒的时候服务器断电了怎么办?是不是这59秒内对数据库的所有操作都不会提交到持久化的数据库文件?丢失59秒的数据,这还不是最可怕的. 如果在60秒后,在进行flushAll的过程中系统宕机,则会造成数据文件错乱,一部分是新数据,一部分是旧数据,这种情况下,有可能我们的数据库就不能用了。

  不知道为什么,MongoDB在正确的退出流程中(调用dbexit(EXIT_CLEAN)),非"--dur模式启动 也并没有调用MemoryMappedFile::flushAll来进行持久化操作,这令我非常费解.一开始我以为是我这个版本的代码没有完善,立马又查阅了2.2版本的源码,发现也并没有在非"--dur"调用flush方法。都仅仅是调用MemoryMappedFile::closeAllFiles.

我个人的理解是,在生产环境下一定会开启"--dur",甚至在新版本中在64位运行环境下默认开启,所以给非dur模式下来一次flush就不那么必要了.

  如果您在使用MongoDB的windows版本进行调试的以验证我上面的描述的话,您会得到相反的结果,可能你的第一感觉就会是我完全的搞错了。的确,一般的人都会这样认为,我们来进行一次简单的测试流程:

  • 以非"--dur"模式启动Mongod,启动时最好调整一下--syncdelay,设置一个较大值如600
  • 使用mogo对数据库的数据进行修改(如修改删除)
  • 使用任务管理器强制结束进程mongod(模拟系统宕机)
  • 删除掉mongod.lock(模拟宕机一定会留下这个),重新启动非"--dur"模式的Mongod
  • 使用mongo进行db.collectiob.find()观察第一次的更改是否已经生效

  使用上述测试流程,您会惊奇的发现,我们的任何更改都已经持久化了,这样是不是就说明我前面所提到的都是胡扯呢?起初我自己也有点怀疑这个结果,反复的测试了很多遍,并进行了跟踪调试,我发现即便MongoDB没有运行过一次flushAll,并且连任何一个MongoMMF类的对象(代表一个数据库记录文件)也不曾调用flush()方法,所做的更改仍然能被持久化。至此,我开始怀疑Windows上并不是显示调用flush才会持久化,而是memcopy更改时就会被持久化,搜索了一下网上,发现了别人在Windows也遇到了相同的问题.(CSDN上命名为 "内存映射,没有FlushViewOfFile,也可以保存到文件"的贴子也遇到了相同的问题).

  对于Windows这个特例,我也就不再深究了,大家知道是这个地方的问题就OK了,其实在它的这种机制下,整个用于flush数据到磁盘的DataFileSync线程都不用,对于Linux,Unix,我上面的总结还是正确的.

问题的解决:

  事实上曾经有人就是因为上面提到的问题丢失了所有数据,所以MongoDB的团队成员才在1.7版本的最新分支上开始对单机高可靠性的提升,这就是引入的Journal\durability模块,着重解决这个问题。(导火索见文章"MongoDB的数据可靠性,单机可靠性有望在1.8版本后增强“)

  在MongoDB源码概述——日志 一文中也提到这个Journal\durability模块,不过最后还有一部分没有讲完,下次将会有专门的博文介绍后续问题。


博客地址:Zealot Yin

欢迎转载,转载请注明出处[http://creator.cnblogs.com/]


涂宗勋 CSDN认证博客专家 web安全 系统安全 安全架构
【若想不迷路,记得点关注,动动小手指,点点全是福】

6年java工作经验,现居湖北武汉,有过支付、OA、CA、OAUTH2等工作经验,现从事车联网行业。
爱好分享,个人博客blog.tzxcode.cn,微信tuzongxun,qq1160569243,欢迎来撩。

专为程序员设计的数学课

11-11
<p> 限时福利限时福利,<span>15000+程序员的选择!</span> </p> <p> 购课后添加学习助手(微信号:csdn590),按提示消息领取编程大礼包!并获取讲师答疑服务! </p> <p> <br> </p> <p> 套餐中一共包含5门程序员必学的数学课程(共47讲) </p> <p> 课程1:《零基础入门微积分》 </p> <p> 课程2:《数理统计与概率论》 </p> <p> 课程3:《代码学习线性代数》 </p> <p> 课程4:《数据处理的最优化》 </p> <p> 课程5:《马尔可夫随机过程》 </p> <p> <br> </p> <p> 哪些人适合学习这门课程? </p> <p> 1)大学生,平时只学习了数学理论,并未接触如何应用数学解决编程问题; </p> <p> 2)对算法、数据结构掌握程度薄弱的人,数学可以让你更好的理解算法、数据结构原理及应用; </p> <p> 3)看不懂大牛代码设计思想的人,因为所有的程序设计底层逻辑都是数学; </p> <p> 4)想学习新技术,如:人工智能、机器学习、深度学习等,这门课程是你的必修课程; </p> <p> 5)想修炼更好的编程内功,在遇到问题时可以灵活的应用数学思维解决问题。 </p> <p> <br> </p> <p> 在这门「专为程序员设计的数学课」系列课中,我们保证你能收获到这些:<br> <br> <span> </span> </p> <p class="ql-long-24357476"> <span class="ql-author-24357476">①价值300元编程课程大礼包</span> </p> <p class="ql-long-24357476"> <span class="ql-author-24357476">②应用数学优化代码的实操方法</span> </p> <p class="ql-long-24357476"> <span class="ql-author-24357476">③数学理论在编程实战中的应用</span> </p> <p class="ql-long-24357476"> <span class="ql-author-24357476">④程序员必学的5大数学知识</span> </p> <p class="ql-long-24357476"> <span class="ql-author-24357476">⑤人工智能领域必修数学课</span> </p> <p> <br> 备注:此课程只讲程序员所需要的数学,即使你数学基础薄弱,也能听懂,只需要初中的数学知识就足矣。<br> <br> 如何听课? </p> <p> 1、登录CSDN学院 APP 在我的课程中进行学习; </p> <p> 2、登录CSDN学院官网。 </p> <p> <br> </p> <p> 购课后如何领取免费赠送的编程大礼包和加入答疑群? </p> <p> 购课后,添加助教微信:<span> csdn590</span>,按提示领取编程大礼包,或观看付费视频的第一节内容扫码进群答疑交流! </p> <p> <img src="https://img-bss.csdn.net/201912251155398753.jpg" alt=""> </p>
©️2020 CSDN 皮肤主题: 成长之路 设计师: Amelia_0503 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值