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,一经查实,立即删除!

相关文章

SharePoint 2010 Form Authentication (SQL) based on existing database

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

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

最新版2.6.7下载&#xff1a;https://download.csdn.net/download/mo3408/88656497 小狐狸GPT付费体验系统如何升级&#xff0c;该系统更新比较频繁&#xff0c;也造成了特别有用户数据情况下升级时麻烦&#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;…

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…

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

骑士游历问题问题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…

《dp补卡——01背包问题》

目录01背包[416. 分割等和子集](https://leetcode-cn.com/problems/partition-equal-subset-sum/)[1049. 最后一块石头的重量 II](https://leetcode-cn.com/problems/last-stone-weight-ii/)[494. 目标和](https://leetcode-cn.com/problems/target-sum/)01背包 1、dp数组以及…

用JavaScript往DIV动态添加内容

参考&#xff1a;http://zhidao.baidu.com/link?url6jSchyqPiEYCBoKdOmv52YHz9r7MTBms2pK1N6ptOX1kaR2eg320mlW1Sr6n36hpOeOadBxC2rWWGuhZPbms-K <div id"show"></div>要填充的数据为: 这是一个测试例子.jquery&#xff1a;$(function(){ var data …

《dp补卡——完全背包问题》

N件物品和一个最多能背重量为W的背包。第i件物品的重量为weight[i]&#xff0c;得到的价值是value[i]。每件物品都有无限个(可以放入背包多次)&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 01背包和完全背包唯一不同在于遍历顺序上。 01背包的核心代码&#xff1a…

《dp补卡——多重背包》

多重背包简介&#xff1a; 有N种物品和一个容量为V的背包。第i种物品最多有Mi件可用&#xff0c;每件耗费的空间为Ci&#xff0c;价值为Wi。求解将哪些物品装入背包可使得这些物品耗费的空间总和不超过背包容量&#xff0c;且价值总和最大。 将Mi件摊开&#xff0c;就是一个01背…

CocoaAsyncSocket 套接字

CocoaAsyncSocket 套接字 https://github.com/robbiehanson/CocoaAsyncSocket Asynchronous socket networking library for Mac and iOS 用于iOS以及Mac的异步套接字网络库。 TCP GCDAsyncSocket and AsyncSocket are TCP/IP socket networking libraries. Here are the key…

谷歌浏览器设置缓存方法

谷歌浏览器设置缓存方法&#xff1a; 1、在桌面Google Chrome快捷方式&#xff0c;目标&#xff1a;找到 C:\Users\Splendid\AppData\Local\…\Application\chrome.exe 在这后面加上-Disk-Cache-Dir”Z:\TEMP” 注意: -Disk前面有空格&#xff0c;”Z:\TEMP” 是文件存放在Z盘T…

《dp补卡——子序列问题》

目录300. 最长递增子序列674. 最长连续递增序列718. 最长重复子数组1143. 最长公共子序列53. 最大子序和392. 判断子序列115. 不同的子序列583. 两个字符串的删除操作72. 编辑距离647. 回文子串 &#xff08;与 5.最长回文子串思路差不多&#xff09;516. 最长回文子序列300. 最…

《leetcode : 647. 回文子串 思考分析双指针解法》

647. 回文子串 如何确定是回文串&#xff1a; 找中心然后往两边扩散&#xff0c;判断是否对称即可。 在遍历中心点的时候&#xff0c;注意中心点可以是一个元素也可以是两个元素。 class Solution { public:int cal_two_extend(const string& s,int i,int j,int n){int re…

explain用法

explain用法 EXPLAIN SELECT …… 变体&#xff1a; 1. EXPLAIN EXTENDED SELECT …… 将执行计划“反编译”成SELECT语句&#xff0c;运行SHOW WARNINGS 可得到被MySQL优化器优化后的查询语句 2. EXPLAIN PARTITIONS SELECT …… 用于分区表的EXPLAIN 执行计划包含的信息 id…

转:Google论文之三----MapReduce

文章来自于&#xff1a;http://www.cnblogs.com/geekma/p/3139823.html MapReduce&#xff1a;大型集群上的简单数据处理 摘要 MapReduce是一个设计模型&#xff0c;也是一个处理和产生海量数据的一个相关实现。用户指定一个用于处理一个键值&#xff08;key-value&#xff09;…

《DBNotes:Join算法的前世今生》

目录NestLoopJoin算法Simple Nested-Loop JoinIndex Nested-Loop JoinBlock Nested-Loop JoinBatched Key AccessHash Join算法In-Memory Join(CHJ)On-Disk Hash Join参考链接在8.0.18之前&#xff0c;MySQL只支持NestLoopJoin算法&#xff0c;最简单的就是Simple NestLoop Joi…

UNITY3D与iOS交互解决方案

原地址&#xff1a;http://bbs.18183.com/thread-456979-1-1.html 本帖最后由 啊,将进酒 于 2014-2-27 11:17 编辑 “授人以鱼&#xff0c;不如授人以渔”&#xff0c;以UNITY3D调用iOS版的91SDK为例&#xff0c;利用C# / C / OBJ-C交互原理,本文将详细介绍UNITY3D与iOS之间交互…