在Mongodb中,其使用了操作系统底层提供的内存映射机制,即MMAP。MMAP可以把磁盘文件的一部分或全部内容直接映射到内存,这样文件中的信息位置就会在内存中有对应的地址空间,这时对文件的读写可以直接用指针来做,而不需要read/write函数了。同时操作系统会将数据刷新保存到磁盘上。如下图:

      


mongodb查看有几个collections mongodb查询大量数据内存满_操作系统

     鉴于linux,window系统为mmap所提供的API大同小异(见下图)。这里仅以mongodb对window系统的mmap调用机制为例,来说明一下其具体的实现方式,以及在mongodb启动时,客户端提交查询和插入操作请求时mongodb的mmap执行流程。

   

mongodb查看有几个collections mongodb查询大量数据内存满_数据库_02



    上面类图中:

    MongoFile:定义了mongo文件对象常用操作,包括创建,关闭,设置名称,flushAll,获取MongoFile文件总尺寸等。

    MMF: 一个类型定义,其声明:typedef MemoryMappedFile MMF;   

    MongoMMF:为了便于journaling/durability操作,对MemoryMappedFile进行了一些封装(特别是对private views )

       

    下面着重看一下windows提供的mmap的常用API:

    MapViewOfFile(): 把文件数据映射到进程的地址空间

    CreateFileMapping() : 创建一个新的文件映射内核对象

    FlushViewOfFile(): 强制系统将内存中修改过的数据重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘

    CloseHandle(): 关闭文件映射对象和文件对象

    MapViewOfFileEx(): 将文件映射到指定的进程地址空间


    参数说明:   

MapViewOfFile(
    __in HANDLE hFileMappingObject,      /*    hFileMappingObject是共享文件对象 
   */ 
   
    __in DWORD dwDesiredAccess,     /*    dwDesiredAccess是文件共享属性 
   */ 
   
    __in DWORD dwFileOffsetHigh,     /*    dwFileOffsetHigh是文件共享区的偏移地址 
   */ 
   
    __in DWORD dwFileOffsetLow,     /*    dwFileOffsetLow是文件共享区的偏移地址 
   */ 
   
    __in SIZE_T dwNumberOfBytesToMap     /*    dwNumberOfBytesToMap是共享数据长度 
   */ 
   
    );   
    
  
//    winbase.h    
        CreateFileMappingW(
    __in      HANDLE hFile,       /*    hFile是创建共享文件的句柄 
   */ 
   
    __in_opt LPSECURITY_ATTRIBUTES lpFileMappingAttributes,     /*    lpFileMappingAttributes是文件共享的属性 
   */ 
   
    __in      DWORD flProtect,      /*    flProtect是当文件映射时读写文件的属性 
   */ 
   
    __in      DWORD dwMaximumSizeHigh,     /*    是文件共享的大小高位字节 
   */ 
   
    __in      DWORD dwMaximumSizeLow,     /*    是文件共享的大小低位字节 
   */ 
   
    __in_opt LPCWSTR lpName     /*    lpName是共享文件对象名称 
   */ 
   
    );
    #ifdef UNICODE
        #define     CreateFileMapping  CreateFileMappingW 
   
        #else    
        #define     CreateFileMapping  CreateFileMappingA 
   
        #endif      
   // 
    !UNICODE  

  
    FlushViewOfFile(
    __in LPCVOID lpBaseAddress,     /*    内存映射文件中的视图的一个字节的地址 
   */ 
   
    __in SIZE_T dwNumberOfBytesToFlush     /*    想要刷新的字节数 
   */ 
   
    );  

  
    MapViewOfFileEx(
    __in HANDLE hFileMappingObject,      /*    共享文件对象 
   */ 
   
    __in DWORD dwDesiredAccess,     /*    文件共享属性 
   */ 
   
    __in DWORD dwFileOffsetHigh,     /*    文件共享区的偏移地址 
   */ 
   
    __in DWORD dwFileOffsetLow,     /*    文件共享区的偏移地址 
   */ 
   
    __in SIZE_T dwNumberOfBytesToMap     /*    共享数据长度 
   */ 
   
    __in_opt LPVOID lpBaseAddress     /*    指定映射文件映射对象的地址。如这个地址处没有足够的内存空间,
                                    那么对MapViewOfFileEx的调用会失效    */    
    );  
    下面我们看一下mongodb如何使用上述API,来实现windows环境下对mongofile进行mmap操作的.
     
     //    mmap_win.cpp 
   
         mutex mapViewMutex(    " 
   mapView 
   " 
   ); 
   // 
   声明mapView的互斥体(mutex)对象 
   
        ourbitset writable;

        /*    * unmapping 通知,以便清空 writable bits  
   */ 
   
        void     MemoryMappedFile::clearWritableBits( 
   void 
     
   * 
   p) {
            for    ( unsigned i  
   = 
    ((size_t)p) 
   / 
   ChunkSize; i  
   <= 
    (((size_t)p) 
   + 
   len) 
   / 
   ChunkSize; i 
   ++ 
    ) {
            writable.clear(i);
            assert(     !    writable. 
   get 
   (i) );
        }
    }

    MemoryMappedFile::MemoryMappedFile()
        : _flushMutex(    new     mutex( 
   " 
   flushMutex 
   " 
   )) {
        fd     =      
   0 
   ;
        maphandle     =      
   0 
   ;
        len     =      
   0 
   ;
        created();
    }
        //    关闭文件MemoryMappedFile 
   
            void 
    MemoryMappedFile::close() {
            for    ( vector 
   < 
   void 
   *> 
   ::iterator i  
   = 
    views.begin(); i  
   != 
    views.end(); i 
   ++ 
    ) {
            clearWritableBits(    *    i);
            UnmapViewOfFile(    *    i);
        }
        views.clear();
            if     ( maphandle )
            CloseHandle(maphandle);    //    关闭文件映射对象和文件对象 
   
            maphandle     = 
     
   0 
   ;
            if     ( fd )
            CloseHandle(fd);    //    关闭文件映射对象和文件对象 
   
            fd     = 
     
   0 
   ;
    }

    unsigned     long      
   long 
    mapped  
   = 
     
   0 
   ;
        //    创建只读map 
   
            void 
   * 
    MemoryMappedFile::createReadOnlyMap() {
        assert( maphandle );
        scoped_lock lk(mapViewMutex);
            void      
   * 
   p  
   = 
    MapViewOfFile(maphandle, FILE_MAP_READ,  
   /* 
   f ofs hi 
   */ 
   0 
   ,  
   /* 
   f ofs lo 
   */ 
     
   0 
   ,  
   /* 
   dwNumberOfBytesToMap 0 means to eof 
   */ 
   0 
   );
            if     ( p  
   == 
     
   0 
    ) {
            DWORD e     =     GetLastError();
            log()     <<      
   " 
   FILE_MAP_READ MapViewOfFile failed  
   " 
     
   << 
    filename()  
   << 
     
   " 
     
   " 
     
   << 
    errnoWithDescription(e)  
   << 
    endl;
        }
            else     {
            views.push_back(p);
        }
            return     p;
    }

        //    创建指定名称和大小的MapViewOfFile 
   
            void 
   * 
    MemoryMappedFile::map( 
   const 
     
   char 
     
   * 
   filenameIn, unsigned  
   long 
     
   long 
     
   & 
   length,  
   int 
    options) {
        assert( fd     ==      
   0 
     
   && 
    len  
   == 
     
   0 
    );  
   // 
    仅能打开一次 
   
            setFilename(filenameIn);
            /*     big hack here: Babble uses db names with colons.  doesn't seem to work on windows.  temporary perhaps.  
   */ 
   
            char     filename[ 
   256 
   ];
        strncpy(filename, filenameIn,     255    );
        filename[    255    ]  
   = 
     
   0 
   ;
        {
            size_t len     =     strlen( filename );
                for     ( size_t i 
   = 
   len 
   - 
   1 
   ; i 
   >= 
   0 
   ; i 
   -- 
    ) {
                    if     ( filename[i]  
   == 
     
   ' 
   / 
   ' 
     
   || 
   
                        filename[i]     ==      
   ' 
   \\ 
   ' 
    )
                        break    ;

                    if     ( filename[i]  
   == 
     
   ' 
   : 
   ' 
    )
                    filename[i]     =      
   ' 
   _ 
   ' 
   ;
            }
        }

        updateLength( filename, length );    //    如果指定文件已存在,则用已存在的文件长度更新length值 
    
   
        {
            DWORD createOptions     =     FILE_ATTRIBUTE_NORMAL;
                if     ( options  
   & 
    SEQUENTIAL )
                createOptions     |=     FILE_FLAG_SEQUENTIAL_SCAN; 
   // 
   针对连续访问对文件缓冲进行优化选项 
   
                DWORD rw     = 
    GENERIC_READ  
   | 
    GENERIC_WRITE; 
   // 
   普通读/写 
   
                fd     = 
    CreateFile( 
   // 
   创建相关文件 
   
                         toNativeString(filename).c_str(),    // 
   创建的文件名称 
   
                         rw,     // 
    desired access 
   
                         FILE_SHARE_WRITE     | 
    FILE_SHARE_READ,  
   // 
    share mode 
   
                         NULL,     // 
    security 
   
                         OPEN_ALWAYS,     // 
    create disposition 
   
                         createOptions ,     // 
    flags 
   
                         NULL);     // 
    hTempl 
   
                    if 
    ( fd  
   == 
    INVALID_HANDLE_VALUE ) {
                DWORD e     =     GetLastError();
                log()     <<      
   " 
   Create/OpenFile failed  
   " 
     
   << 
    filename  
   << 
     
   " 
    errno: 
   " 
     
   << 
    e  
   << 
    endl;
                    return      
   0 
   ;
            }
        }

        mapped     +=     length;
        {

               //    采用“读写文件数据”方式的页面保护属性           
               DWORD flProtect = PAGE_READWRITE;
    
                //    创建一个文件映射内核对象并告诉系统文件的尺寸以及访问文件的方式 
   
                maphandle     = 
    CreateFileMapping(fd, NULL, flProtect,
                                          length     >>      
   32 
     
   /* 
   maxsizehigh 
   */ 
   ,
                                          (unsigned) length     /*    maxsizelow 
   */ 
   ,
                                          NULL    /*    lpName 
   */ 
   );
                if     ( maphandle  
   == 
    NULL ) {

                   //     先获取操作信息, 因为下面的log()要删除lasterror信息 
   
                    DWORD e     = 
    GetLastError();
                log()     <<      
   " 
   CreateFileMapping failed  
   " 
     
   << 
    filename  
   << 
     
   ' 
     
   ' 
     
   << 
    errnoWithDescription(e)  
   << 
    endl;
                close();
                    return      
   0 
   ;
            }
        }

            void      
   * 
   view  
   = 
     
   0 
   ;
        {
            scoped_lock lk(mapViewMutex);
            DWORD access     =     (options 
   & 
   READONLY) 
   ? 
    FILE_MAP_READ : FILE_MAP_ALL_ACCESS;

                //    把文件数据映射到进程的地址空间 
   
                view     = 
    MapViewOfFile(maphandle, access,  
   /* 
   f ofs hi 
   */ 
   0 
   ,  
   /* 
   f ofs lo 
   */ 
     
   0 
   ,  
   /* 
   dwNumberOfBytesToMap 0 means to eof 
   */ 
   0 
   );
        }
            if     ( view  
   == 
     
   0 
    ) {
            DWORD e     =     GetLastError();
            log()     <<      
   " 
   MapViewOfFile failed  
   " 
     
   << 
    filename  
   << 
     
   " 
     
   " 
     
   << 
    errnoWithDescription(e)  
   << 
    endl;
            close();
        }
            else     {
            views.push_back(view);
        }
        len     =     length;

            return     view;
    }

        class     WindowsFlushable :  
   public 
    MemoryMappedFile::Flushable {
        public    :
        WindowsFlushable(     void      
   * 
    view , HANDLE fd ,  
   string 
    filename , boost::shared_ptr 
   < 
   mutex 
   > 
    flushMutex )
            : _view(view) , _fd(fd) , _filename(filename) , _flushMutex(flushMutex)
        {}

            void     flush() {
                if     ( 
   ! 
   _view  
   || 
     
   ! 
   _fd)
                    return    ;

            scoped_lock lk(    *    _flushMutex);
                //     强制系统将内存中修改过的数据重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘。 
   
                    bool 
    success  
   = 
    FlushViewOfFile(_view,  
   0 
     
   /* 
   0表示全部mapping 
   */ 
   );
                if     ( 
   ! 
   success) {
                    int     err  
   = 
    GetLastError();
                    out    ()  
   << 
     
   " 
   FlushViewOfFile failed  
   " 
     
   << 
    err  
   << 
     
   " 
    file:  
   " 
     
   << 
    _filename  
   << 
    endl;
            }

            success     =     FlushFileBuffers(_fd); 
   // 
   刷新内部文件缓冲区的数据刷到磁盘上 
   
                    if 
    ( 
   ! 
   success) {
                    int     err  
   = 
    GetLastError();
                    out    ()  
   << 
     
   " 
   FlushFileBuffers failed  
   " 
     
   << 
    err  
   << 
     
   " 
    file:  
   " 
     
   << 
    _filename  
   << 
    endl;
            }
        }

            void      
   * 
    _view;
        HANDLE _fd;
            string     _filename;
        boost::shared_ptr    <    mutex 
   > 
    _flushMutex;
    };
        //    是否进行异步的flush操作(该操作会将修改过的数据部分或全部重新写入磁盘映像) 
   
            void 
    MemoryMappedFile::flush( 
   bool 
    sync) {
     uassert(    13056    ,  
   " 
   Async flushing not supported on windows 
   " 
   , sync); 
   // 
   windows系统不支持异步flush 
   
                if 
   (  
   ! 
   views.empty() ) {
            WindowsFlushable f( views[    0    ] , fd , filename() , _flushMutex);
            f.flush();
        }
    }
       //    预先刷数据操作,该方法确保这个对象是可以执行flush()操作,以便在调用该方法之后执行flush操作.
       //    参见mmap.cpp flushAll操作 
   
   MemoryMappedFile::Flushable     *     MemoryMappedFile::prepareFlush() {
            return      
   new 
    WindowsFlushable( views.empty()  
   ? 
     
   0 
    : views[ 
   0 
   ] , fd , filename() , _flushMutex );
    }
        void     MemoryMappedFile::_lock() {}
        void     MemoryMappedFile::_unlock() {}


    上面的代码比较简单,大家看一下注释就可以了,下面看一下mmf对于上面的MemoryMappedFile类实现是如何封装的,因为mmf会在journaling/durability这类场景下使用PrivateMap():   
 


//    mongommf.cpp文件
        //    构造PrivateMap 
   
            void 
   * 
    MemoryMappedFile::createPrivateMap() {
        assert( maphandle );
        scoped_lock lk(mapViewMutex);
            //    void *p = mapaligned(maphandle, len); 
   
                void 
     
   * 
   p  
   = 
    MapViewOfFile(maphandle, FILE_MAP_READ,  
   0 
   ,  
   0 
   ,  
   0 
   );
            if     ( p  
   == 
     
   0 
    ) {
            DWORD e     =     GetLastError();
            log()     <<      
   " 
   createPrivateMap failed  
   " 
     
   << 
    filename()  
   << 
     
   " 
     
   " 
     
   << 
    errnoWithDescription(e)  
   << 
    endl;
        }
            else     {
            clearWritableBits(p);
            views.push_back(p);
        }
            return     p;
    }
        //    重新映射PrivateView 
   
            void 
   * 
    MemoryMappedFile::remapPrivateView( 
   void 
     
   * 
   oldPrivateAddr) {
        dbMutex.assertWriteLocked();     //     short window where we are unmapped so must be exclusive

            //     mapViewMutex确保在重新映射时获得相同的地址 
   
            scoped_lock lk(mapViewMutex);
            //    清空 writable bits 
   
            clearWritableBits(oldPrivateAddr);
            //    从进程的地址空间(oldPrivateAddr)撤消文件数据的映像 
   
                if 
   (  
   ! 
   UnmapViewOfFile(oldPrivateAddr) ) {
            DWORD e     =     GetLastError();
            log()     <<      
   " 
   UnMapViewOfFile failed  
   " 
     
   << 
    filename()  
   << 
     
   ' 
     
   ' 
     
   << 
    errnoWithDescription(e)  
   << 
    endl;
            assert(    false    );
        }

            //     将文件映射到指定的进程地址空间 
   
                void 
     
   * 
   p  
   = 
    MapViewOfFileEx(maphandle, FILE_MAP_READ,  
   0 
   ,  
   0 
   ,
                                      /*    dwNumberOfBytesToMap 0 means to eof 
   */ 
   0 
     
   /* 
   len 
   */ 
   ,
                                  oldPrivateAddr);
        
            if     ( p  
   == 
     
   0 
    ) {
            DWORD e     =     GetLastError();
            log()     <<      
   " 
   MapViewOfFileEx failed  
   " 
     
   << 
    filename()  
   << 
     
   " 
     
   " 
     
   << 
    errnoWithDescription(e)  
   << 
    endl;
            assert(p);
        }
        assert(p     ==     oldPrivateAddr);
            return     p;
    }
    #endif    
        //    重新映射PrivateView 
   
            void 
    MongoMMF::remapThePrivateView() {
        assert( cmdLine.dur );

            //     todo 1.9 : it turns out we require that we always remap to the same address.
            //     so the remove / add isn't necessary and can be removed 
   
            privateViews.remove(_view_private);
        _view_private     =     remapPrivateView(_view_private);
        privateViews.add(_view_private,     this    );
    }
    ......

        //    打开指定的文件并执行mmap操作 
   
            bool 
    MongoMMF::open( 
   string 
    fname,  
   bool 
    sequentialHint) {
        setPath(fname);
        _view_write     =     mapWithOptions(fname.c_str(), sequentialHint  
   ? 
    SEQUENTIAL :  
   0 
   );
            return     finishOpening();
    }
        //    创建指定名称的文件并执行mmap操作 
   
            bool 
    MongoMMF::create( 
   string 
    fname, unsigned  
   long 
     
   long 
   & 
    len,  
   bool 
    sequentialHint) {
        setPath(fname);
        _view_write     =     map(fname.c_str(), len, sequentialHint  
   ? 
    SEQUENTIAL :  
   0 
   );
            return     finishOpening();
    }
        //    创建PrivateMap并加载到privateViews集合中 
   
            bool 
    MongoMMF::finishOpening() {
            if    ( _view_write ) {
                if    ( cmdLine.dur ) {
                _view_private     =     createPrivateMap();
                    if    ( _view_private  
   == 
     
   0 
    ) {
                    massert(     13636     ,  
   " 
   createPrivateMap failed (look in log for error) 
   " 
    ,  
   false 
    );
                }
                privateViews.add(_view_private,     this    );  
   // 
    note that testIntent builds use this, even though it points to view_write then... 
   
                }
                else     {
                _view_private     =     _view_write;
            }
                return      
   true 
   ;
        }
            return      
   false 
   ;
    }
    ......
        //    从privateViews集合中移除当前 _view_private,并关闭文件映射对象和文件对象 
   
            void 
    MongoMMF::close() {
        {
                if    ( cmdLine.dur  
   && 
    _view_write 
   /* 
   actually was opened 
   */ 
    ) {
                    if    ( debug )
                    log()     <<      
   " 
   closingFileNotication: 
   " 
     
   << 
    filename()  
   << 
    endl;
                dur::closingFileNotification();
            }
            privateViews.remove(_view_private);
        }
        _view_write     =     _view_private  
   = 
     
   0 
   ;
        MemoryMappedFile::close();    //    关闭文件映射对象和文件对象 
   
        }


 

    mongodb完成了上面的工具类的声明定义之后,就会在前台使用这些类了,下面通过插入数据操作(之前主要流程我已在这篇文章中有所描述)过程中,对上面类的使用来进行阐述.  

    首先需要说明的是,如果是首次在本地运行mongod,则不会在指定的数据库目录(dbpath 参数)下生成数据库文件,但如果有数据插入时,则会生成相应文件,这里可以理解为生成文件的过程就是mmap的创建过程。
    
    之前的文章中提到过,当客户端要插入记录时,则系统会根据客户端的操作枚举信息来调用相应的操作,这里它会执行instance.cpp文件中的receivedInsert方法,并进而调用 pdfile.cpp 文件的 insert()函数,而在该方法下有如下一段代码:


DiskLoc DataFileMgr::insert(    const      
   char 
     
   * 
   ns,  
   const 
     
   void 
     
   * 
   obuf,  
   int 
    len,  
   bool 
    god,  
   const 
    BSONElement  
   & 
   writeId,  
   bool 
    mayAddIndex) {
  ......
        NamespaceDetails     *    d  
   = 
    nsdetails(ns); 
   // 
   获取ns的详细信息 
   
                if 
    ( d  
   == 
     
   0 
    ) {
            addNewNamespaceToCatalog(ns);    //    向system catalog添加新的名空间,它会再次调用当前insert()方法
          
                //     创建第一个数据库文件,方法位于database.cpp 
   
                cc().database()    -> 
   allocExtent(ns, Extent::initialSize(len),  
   false 
   ); 
    
   
      ......
  }


    上面的allocExtent方法用于分配Extent要求的磁盘空间,其中Extent用于记录多个record记录信息,而record就是数据库中的一条记录。这里可以将Extent看成是一个数据集合,但与我们通常所理解的"数据表"(datatable)有所差异,因为在同一个namespace下可以有一个或多个extent(可以不连续),extent之间是一个双向链表结构,其通过cursor进行向前(forward)或反转(reverse)的访问。有关这些内容,参见我之前写的这篇文章。  

    言归正传,在上面的allocExtent方法中,会执行pdfile.cpp中的如下方法:


//    pdfile.cpp 
   
        Extent    * 
    MongoDataFile::createExtent( 
   const 
     
   char 
     
   * 
   ns,  
   int 
    approxSize,  
   bool 
    newCapped,  
   int 
    loops) {
        .....
            int     ExtentSize  
   = 
    approxSize  
   <= 
    header() 
   -> 
   unusedLength  
   ? 
    approxSize : header() 
   -> 
   unusedLength;
        DiskLoc loc;
            if     ( ExtentSize  
   < 
    Extent::minSize() ) { 
   // 
   判断当前ExtentSize的大小 
   
                ......
                //    addAFile方法位于 database.cpp 
   
                    return 
    cc().database() 
   -> 
   addAFile(  
   0 
   ,  
   true 
    ) 
   -> 
   createExtent(ns, approxSize, newCapped, loops 
   + 
   1 
   );
        .....

    }   
 
    最后在addAFile方法中,我们会看下如下代码段:
     
     //    database.cpp 
   
        MongoDataFile    * 
    Database::addAFile(  
   int 
    sizeNeeded,  
   bool 
    preallocateNextFile ) {
            int     n  
   = 
    ( 
   int 
   ) files.size();
        MongoDataFile     *    ret  
   = 
    getFile( n, sizeNeeded ); 
   // 
   调用下面的getFile方法 
   
            .....
    }

        //    database.cpp   
   
        MongoDataFile    * 
    Database::getFile(  
   int 
    n,  
   int 
    sizeNeeded ,  
   bool 
    preallocateOnly) {
        ......
        namespaceIndex.init();
        .....
    }
    
        //    namespace.cpp   
   
            void 
    NamespaceIndex::init() {
        ......
        unsigned     long      
   long 
    len  
   = 
     
   0 
   ;
        boost::filesystem::path nsPath     =     path();
            string     pathString  
   = 
    nsPath. 
   string 
   ();
            void      
   * 
   p  
   = 
     
   0 
   ;
            if    ( MMF::exists(nsPath) ) { 
   // 
   使用本文前面提到的MMF类,判断数据库文件是否存在 
   
                    if 
   ( f.open(pathString,  
   true 
   ) ) { 
   // 
   打开指定的文件并执行mmap操作 
   
                    len     = 
    f.length();
                    if     ( len  
   % 
    ( 
   1024 
   * 
   1024 
   )  
   != 
     
   0 
    ) {
                    log()     <<      
   " 
   bad .ns file:  
   " 
     
   << 
    pathString  
   << 
    endl;
                    uassert(     10079     ,   
   " 
   bad .ns file length, cannot open database 
   " 
   , len  
   % 
    ( 
   1024 
   * 
   1024 
   )  
   == 
     
   0 
    );
                }
                p     =     f.getView(); 
   // 
   返回mapview 
   
                }
        }
            else     { 
   // 
   不存在
                //     use lenForNewNsFiles, we are making a new database 
   
                massert(     10343 
   ,  
   " 
   bad lenForNewNsFiles 
   " 
   , lenForNewNsFiles  
   >= 
     
   1024 
   * 
   1024 
    );
            maybeMkdir();    //    创建相应目录(如不存在) 
   
                unsigned     long 
     
   long 
    l  
   = 
    lenForNewNsFiles;
                if    ( f.create(pathString, l,  
   true 
   ) ) { 
   // 
   创建指定名称的文件并执行mmap操作 
   
                    getDur().createdFile(pathString, l);     // 
    always a new file 
   
                    len     = 
    l;
                assert( len     ==     lenForNewNsFiles );
                p     =     f.getView(); 
   // 
   返回mapview 
   
                }
        }
        ......
    }

    下面用一张时序图来大体回顾一下这一流程:

  

mongodb查看有几个collections mongodb查询大量数据内存满_数据库_03



    在创建了该数据库文件及相应mmap操作之后,下面再重新启动mongod时,系统会通过构造client类的上下文对象 (context)方法来最终调用namespaceIndex.init()方法,其时序图如下,大家可以通过调试源码来难证这一流程:

  

mongodb查看有几个collections mongodb查询大量数据内存满_数据库_04



    好了,今天的内容到这里就告一段落。




    作者: daizhj, 代震军