设为首页 收藏本站
查看: 847|回复: 0

[经验分享] MongoDB源码概述——使用日志提升单机数据可靠性

[复制链接]

尚未签到

发表于 2015-7-7 11:13:45 | 显示全部楼层 |阅读模式
  在MongoDB源码概述——内存管理和存储引擎一文的最后,我们留下了一个问题,在使用MongoDB的内存管理与存储引擎时,因为其依仗操作系统的MMAP方式,将磁盘上的文件映射到进程的内存空间,这给MongoDB带来了极大的便利,可也给我们带来了不小的问题。到底隔多久一次将映射的在内存的视图持久化硬盘才能保证我们服务器在宕机时丢失的数据最少呢?针对flushAll过程中宕机有可能造成的数据错乱,有没有什么好的解决方案呢?
  MongoDB的团队成员1.7版本的最新分支上开始对单机高可靠性的提升,这就是引入的Journal\Durability模块,这个模块主要解决上面提出来的问题,对提高单机数据的可靠性,起了决定性的作用.其机制主要是通过log方式定时将操作日志(对数据库有更改的操作,查询不在记录范围之类)记录到dbpath的命名为journal文件夹下,这样当系统再次重启时从该文件夹下恢复丢失的数据。
  下面我们针对源码,对他进行简要分析

Journal记录模块
  Journal\durability模块的调用路径如下:
  Main()——》initAndListen()——》_initAndListen()——》dur::startup();
  Startup()的代码如下:



        void startup() {
if( !cmdLine.dur )
return;
//DurableInterface的工厂模式   用DurableImpl来实例化getDur获取的对象
DurableInterface::enableDurability();
journalMakeDir();//确认日志目录
try {
recover();//修复模式
}
catch(...) {
log() serialize(bb);
}

for( set::iterator i = commitJob.writes().begin(); i != commitJob.writes().end(); i++ ) {
prepBasicWrite_inlock(bb, &(*i), lastDbPath);
}
  通过上面代码我们可以得知,DurOp的序列化是自己的serialize方法完成的,他们的序列化操作不牵扯到被修改数据,所以序列化结果可以很简洁。例如一个DropDbOp,卸载掉某个数据库,如果需要恢复,指向需要再运行一次卸载过程即可,所以只需要用一个东西(甚至代码数字也可以)来标识就行了。而BasicWrites就不一样了,例如新插入了一个Record,我们需要记录下整个Record作为恢复的数据源,没错,这就是上面没有解释的代码prepBasicWrite_inlock所干的事情。



    JEntry e;
...
bb.appendStruct(e);
bb.appendBuf(i->start(), e.len)
  对于AlignedBuilder,我们可以理解为我们序列化过程中的Buf,存储着已经序列化好的待持久化的数据,appendBuf将会将参数指定位置的数据进行memcopy,实际上这里说是序列化还有些问题,像这些数据源,存储在journal日志文件时就是二进制。好了,不纠结这个名称了,AlignedBuilder除了放入了数据源之外,还放入了JEntry来表示一些基本属性,JEntry与WriteIntent是1:1的关系,也只有这样,读取的时候才能正确的寻址.
  在全部序列化之后,系统调用WRITETOJOURNAL(commitJob._ab)来将AlignedBuilder持久化到journal日志文件,最终通过调用LogFile::synchronousAppend负责向外部存储文件写入。接着系统调用WRITETODATAFILES(),事实上我在第一次看源码的时候我非常的不解,举个例子,我们在插入数据的时候,已经将将用户要插入的数据memcopy过一次了,已经存到内存里面的视图(View)上了,为什么这里的WRITETODATAFILES还需要memcopy一次呢?我在这个问题上也纠结了很久,最后才找到了答案。这个奥秘就在于如果启用了dur模式,对于每个MemoryMappedFile实际上会产生两个视图,一个_view_private,一个_view_write(对于未开启dur模式的mongodb在32bit系统上运行,官方说db数据不能超过2.5G,现在通过这个原理我们可以看到他的水分,实际上最优的大小也就1G)。代码如下:



bool MongoMMF::finishOpening() {
if( _view_write ) {//_view_write先创建
if( cmdLine.dur ) {
_view_private = createPrivateMap();//创建 _view_private
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 {//若不允许dur  则只用一个view
_view_private = _view_write;
}
return true;
}
return false;
}

  MongoMMF内的两个视图,只有一个能被Flush到磁盘,那就是第一个创建的视图,_view_write一定是第一个创建的,所以只有他才能真正持久化。



void MemoryMappedFile::flush(bool sync) {
uassert(13056, "Async flushing not supported on windows", sync);
if( !views.empty() ) {
WindowsFlushable f( views[0] , fd , filename() , _flushMutex);
f.flush();
}
}
  我们现在还只是知道dur模式有两次memcopy,可是为什么会有两次呢?从此模式下有两个不同的视图出发,你有没有想到什么?没错,我们在Insert方法中(pdfile.cpp 1596行)调用memcopy是将内容复制到_view_private上(pdfile.cpp 1596行可知recordAt使用的是p,p=》_mb=》 _mb = mmf.getView();所以,实际上那个record在view_private上),不是可以被持久化的_view_write,所以在WRITETODATAFILES需要在复制一次,而此时,数据源则就是view_private上被复制过来的数据.
  通过上面两段的代码,我们还能发现,在非dur模式下,_view_private与_view_write实际上是同一个东西。这也就解释了为什么非dur模式不需要两次memcopy就能很好的完成工作(非dur模式不运行WRITETODATAFILES)。
  好,至此为止,我们所有的结论都已经对接上了。
  最后用一张非常蹩脚的时序图来描述这一过程(过程非完全面向对象)。
DSC0000.png

Journal恢复模块
  此模块在系统启动时运行,他完成对上次宕机遗留的Journal文件进行解读(也是通过MMAP的方式)并将没有Flush到数据库记录文件的记录重新通过memcopy的方式放入_view_write中。以备存储引擎线程执行持久化。
  若系统上次是正常退出,则在退出流程中会进行最后的Flush(仅dur模式),并清理现有的Journal文件,所以正常退出是不会遗留任何的Journal文件的.
  这个部分的操作也非常的简单,因为时间的关系,本文就不再详细阐述了,时序图如下:
DSC0001.png
  

  
  不早了. 洗洗睡了!!!
  另寻找热爱底层技术(C/C++ linux)的朋友一起研究和创造有意思的东西!

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-84071-1-1.html 上篇帖子: Tokumx 安装指南(做法如同MongoDB) 下篇帖子: [mongodb翻译]选择合适的shard key
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表