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

数据存储:

  之前在介绍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)中需要注意一下几个方法:

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数据库文件到内存:

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

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

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

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()。

void run() {if( cmdLine.syncdelay == 0 )log() << "warning: --syncdelay 0 is not recommended and can have strange performance" << endl;else if( cmdLine.syncdelay == 1 )log() << "--syncdelay 1" << endl;else if( cmdLine.syncdelay != 60 )//默认是60log(1) << "--syncdelay " << cmdLine.syncdelay << endl;int time_flushing = 0;while ( ! inShutdown() ) {flushDiagLog();if ( cmdLine.syncdelay == 0 ) {// in case at some point we add an option to change at runtimesleepsecs(5);continue;}sleepmillis( (long long) std::max(0.0, (cmdLine.syncdelay * 1000) - time_flushing) );if ( inShutdown() ) {// occasional issue trying to flush during shutdown when sleep interruptedbreak;}Date_t start = jsTime();//当前dataFileSync的任务就是在一段时间后(cmdLine.syncdelay)将内存中的数据flush到磁盘上(因为mongodb使用mmap方式将数据先放入内存中)int numFiles = MemoryMappedFile::flushAll( true );time_flushing = (int) (jsTime() - start);globalFlushCounters.flushed(time_flushing);log(1) << "flushing mmap took " << time_flushing << "ms " << " for " << numFiles << " files" << endl;}}

  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模块,不过最后还有一部分没有讲完,下次将会有专门的博文介绍后续问题。

转载于:https://www.cnblogs.com/Creator/archive/2012/11/04/2754110.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377124.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OBTW的完整形式是什么?

OBTW&#xff1a;哦&#xff0c;顺便说一下 (OBTW: Oh, By The Way) OBTW is an abbreviation of "Oh, By The Way". OBTW是“哦&#xff0c;顺便说一下”的缩写 。 It is an expression, which is commonly used in messaging or chatting on social media network…

SharePoint 2010 Form Authentication (SQL) based on existing database

博客地址 http://blog.csdn.net/foxdaveSharePoint 2010 表单认证&#xff0c;基于现有数据库的用户信息表本文主要描述本人配置过程中涉及到的步骤&#xff0c;仅作为参考&#xff0c;不要仅限于此步骤。另外本文通俗易懂&#xff0c;适合大众口味儿。I. 开启并配置基于声明的…

《MySQL 8.0.22执行器源码分析(3.1)关于RowIterator》

目录RowIteratorInit()Read()SetNullRowFlag()UnlockRow()StartPSIBatchMode()EndPSIBatchModeIfStarted()real_iterator()RowIterator 使用选定的访问方法读取单个表的上下文&#xff1a;索引读取&#xff0c;扫描等&#xff0c;缓存的使用等。 它主要是用作接口&#xff0c;但…

hdu 2432法里数列

这题本来完全没思路的&#xff0c;后来想一想&#xff0c;要不打个表找找规律吧。于是打了个表&#xff0c;真找到规律了。。。 打表的代码如下&#xff1a; int n; void dfs(int x1, int y1, int x2, int y2) {if (y1 y2 < n) {dfs(x1, y1, x1 x2, y1 y2);printf("…

python学习笔记四——数据类型

1.数字类型&#xff1a; 2.字符串类型&#xff1a; 切片&#xff1a;a[m:n:s] m:起始值 n:结束值&#xff08;不包括n&#xff09; s:步长&#xff0c;负数表示从后向前取值 3.序列&#xff1a;列表&#xff0c;元组和字符串都是序列 序列的两个主要特点是索引操作符和切片…

小狐狸ChatGPT系统 不同老版本升级至新版数据库结构同步教程

最新版2.6.7下载&#xff1a;https://download.csdn.net/download/mo3408/88656497 小狐狸GPT付费体验系统如何升级&#xff0c;该系统更新比较频繁&#xff0c;也造成了特别有用户数据情况下升级时麻烦&#xff0c;特别针对会员关心的问题出一篇操作教程&#xff0c;本次教程…

《MySQL 8.0.22执行器源码分析(3.2)关于HashJoinIterator》

在本文章之前&#xff0c;应该了解的概念&#xff1a; 连接的一些概念、NLJ、BNL、HashJoin算法。 目录关于join连接probe行保存概念Hashjoin执行流程&#xff08;十分重要&#xff09;HashJoinIterator成员函数讲解1、BuildHashTable2、ReadNextHashJoinChunk3、ReadRowFromPr…

json 语法_JSON的基本语法

json 语法JSON which stands for JavaScript Object Notation is a lightweight readable data format that is structurally similar to a JavaScript object much like its name suggests. 代表JavaScript Object Notation的 JSON是一种轻量级的可读数据格式&#xff0c;其结…

RFC3261(17 事务)

SIP是一个基于事务处理的协议&#xff1a;部件之间的交互是通过一系列相互独立的消息交换来完成的。特别是&#xff0c;一个SIP 事务由一个单个请求和这个请求的所有应答组成&#xff0c;这些应答包括了零个或者多个临时应答以及一个或者多个终结应答。在事务中&#xff0c;当请…

HDUOJ---1754 I Hate It (线段树之单点更新查区间最大值)

I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 33469 Accepted Submission(s): 13168 Problem Description很多学校流行一种比较的习惯。老师们很喜欢询问&#xff0c;从某某到某某当中&#xff0c;…

WEG的完整形式是什么?

WEG&#xff1a;邪恶邪恶的咧嘴 (WEG: Wicked Evil Grin) WEG is an abbreviation of "Wicked Evil Grin". WEG是“ Wicked Evil Grin”的缩写 。 It is also known as EWG (Evil Wicked Grin) "Grin" refers to a broad smile. "Wicked" refer…

C# 把数字转换成链表

例如&#xff1a;123456转换成 1 -> 2 -> 3-> 4-> 5-> 6 View Code static LinkedList<int> CovertIntToLinkedList(int num){Stack<int> stack new Stack<int>();LinkedList<int> result new LinkedList<int>();while (num!0…

《MySQL 8.0.22执行器源码分析(4.1)Item_sum类以及聚合》

Item_sum类用于SQL聚合函数的特殊表达式基类。 这些表达式是在聚合函数&#xff08;sum、max&#xff09;等帮助下形成的。item_sum类也是window函数的基类。 聚合函数&#xff08;Aggregate Function&#xff09;实现的大部分代码在item_sum.h和item_sum.cc 聚合函数限制 不…

Java 性能优化实战记录(2)---句柄泄漏和监控

前言: Java不存在内存泄漏, 但存在过期引用以及资源泄漏. (个人看法, 请大牛指正) 这边对文件句柄泄漏的场景进行下模拟, 并对此做下简单的分析.如下代码为模拟一个服务进程, 忽略了句柄关闭, 造成不能继续正常服务的小场景. 1 public class FileHandleLeakExample {2 3 p…

什么是Java文件?

Java文件 (Java files) The file is a class of java.io package. 该文件是java.io包的类。 If we create a file then we need to remember one thing before creating a file. First, we need to check whether a file exists of the same name or not. If a file of the sa…

绕过本地验证提交HTML数据

我们在入侵一个网站,比如上传或者自己定义提交的文件时,会在本地的代码中遇到阻碍,,也就是过 滤,过滤有两种,一种是在远程服务器的脚本上进行的过滤,这段代码是在服务器上运行后产生作用的,这种过 滤方式叫做远程过滤;另一种是在我们的IE浏览器里执行的脚本过滤,就是说是在我们…

《dp补卡——343. 整数拆分、96. 不同的二叉搜索树》

343. 整数拆分 1、确定dp数组以及下标含义。 dp[i]&#xff1a;分拆数字i&#xff0c;可以得到的最大的乘积 2、确定递推公式&#xff1a; dp[i]最大乘积出处&#xff1a;从1遍历j到i&#xff0c;j * dp[i-j] 与 j * (i-j)取最大值。( 拆分j的情况&#xff0c;在遍历j的过程…

Adroid学习之 从源码角度分析-禁止使用回退按钮方案

有时候&#xff0c;不能让用户进行回退操作&#xff0c;如何处理&#xff1f; 查看返回键触发了哪些方法。在打开程序后把这个方法禁止了。问题&#xff1a;程序在后台驻留&#xff0c;这样就会出现&#xff0c;其他时候也不能使用回退按钮。如何处理&#xff0c;在onpase()时方…

骑士游历问题问题_骑士步行问题

骑士游历问题问题Problem Statement: 问题陈述&#xff1a; There is a chessboard of size NM and starting position (sx, sy) and destination position (dx,dy). You have to find out how many minimum numbers of moves a knight goes to that destination position? 有…

Android基础之用Eclipse搭建Android开发环境和创建第一个Android项目(Windows平台)...

一、搭建Android开发环境 准备工作&#xff1a;下载Eclipse、JDK、Android SDK、ADT插件 下载地址&#xff1a;Eclipse:http://www.eclipse.org/downloads/ JDK&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/jdk7u9-downloads-1859576.html Android SD…