• MongoDB数据缓存刷新机制详解
  • 连雨独饮 发表于 2015/9/9 12:48:00 | 分类标签: 缓存 MongoDB
  • 最近配合好几个项目测试了MongoDB的写入性能。在内存没有用尽的情况下,虽然MongoDB只有一个更新线程,写入还是非常快的,基本上能达到25000/s以上(索引数据用uuid_generate_randome和uuid_unparse随机产生)。当内存用尽开始往磁盘上刷脏页的时候,性能有非常大的波动,即使调整了syncdelay也没有太大改善。在测试中还出现了一个莫名其妙的情况:MongoDB会间歇性地释放文件系统的cache。除了直接删除表空间之外,很难想到有什么动作可以诱发这个现象。在MongoDB开发者论坛里描述了这个现象,但是Eliot Horowitz认为MongoDB内部并没有代码会释放文件系统cache。那么,让我们去源码里面看一下MongoDB缓存和刷新数据的机制。

    首先找到mongod的入口(db/db.cpp),发现MongoDB的初始化步骤非常简单,概括起来就以下三步:

    HTML 预览

    1. int main(int argc, char* argv[], char *envp[] )  
    2. {  
    3. …  
    4. Module::configAll( params );  
    5. dataFileSync.go();  
    6. …  
    7. initAndListen(cmdLine.port, appsrvPath);  
    8. …  
    9. }  
    显然,dataFileSync就是我们感兴趣的那个类。dataFileSync类派生自BackgroundJob类,而BackgroundJob主要的功能就是生成一个后台线程并指派任务。数据的刷新是一个不断执行的后台任务,在dataFileSync.run()里面可以找到刷数据的相关代码:

    HTML 预览

    1. void run()  
    2. {  
    3. …  
    4. Date_t start = jsTime();  
    5. int numFiles = MemoryMappedFile::flushAll( true );  
    6. time_flushing = (int) (jsTime() – start);  
    7.   
    8. globalFlushCounters.flushed(time_flushing);  
    9. …  
    10. }  
    从这一段代码看,MongoDB会在syncdelay设定的周期内,采取同步的形式刷新所有的脏数据。再看一下flushAll是怎么刷新所有数据的:

    HTML 预览

    1. int MongoFile::flushAll( bool sync )  
    2. {  
    3. …  
    4. set seen;  
    5. while ( true ){  
    6. auto_ptr f;  
    7. {  
    8. rwlock lk( mmmutex , false );  
    9. for ( set::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ){  
    10. MongoFile * mmf = *i;  
    11. if ( ! mmf )  
    12. continue;  
    13. if ( seen.count( mmf ) )  
    14. continue;  
    15. f.reset( mmf->prepareFlush() );  
    16. seen.insert( mmf );  
    17. break;  
    18. }  
    19. }  
    20. if ( ! f.get() )  
    21. break;  
    22.   
    23. f->flush();  
    24. }  
    25. return seen.size();  
    26. }  
    上面这一段代码实现的功能很简单,就是把mmfiles中所有MongoFile指针所引用的对象都flush()一次。不过在执行flush()函数之前,需要先执行prepareFlush()确保这个对象是可以执行flush()函数的。下面是最后真正执行刷新操作的代码:

    HTML 预览

    1. void MemoryMappedFile::flush(bool sync)  
    2. {  
    3. if ( view == 0 || fd == 0 )  
    4. return;  
    5. if ( msync(view, len, sync ? MS_SYNC : MS_ASYNC) )  
    6. problem() << “msync ” << errnoWithDescription() << endl;  
    7. }  
    终于刷新到磁盘了,呵呵。不过这篇blog只涉及到了数据刷新的代码,至于如何缓存,且听下回分解。

  • 请您注意

    ·自觉遵守:爱国、守法、自律、真实、文明的原则

    ·尊重网上道德,遵守《全国人大常委会关于维护互联网安全的决定》及中华人民共和国其他各项有关法律法规

    ·严禁发表危害国家安全,破坏民族团结、国家宗教政策和社会稳定,含侮辱、诽谤、教唆、淫秽等内容的作品

    ·承担一切因您的行为而直接或间接导致的民事或刑事法律责任

    ·您在编程中国社区新闻评论发表的作品,本网站有权在网站内保留、转载、引用或者删除

    ·参与本评论即表明您已经阅读并接受上述条款

  • 感谢本文作者
  • 作者头像
  • 昵称:连雨独饮
  • 加入时间:2013/7/3 0:00:00
  • TA的签名
  • 这家伙很懒,虾米都没写
  • +进入TA的空间
  • 以下内容也很赞哦
分享按钮