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.pswp.cn/news/377124.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/377124.shtml
英文地址,請注明出處:http://en.pswp.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…