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

[经验分享] mongodb持久化

[复制链接]

尚未签到

发表于 2015-7-7 09:34:22 | 显示全部楼层 |阅读模式
DSC0000.jpg
  先上一张图(根据此处重画),看完下面的内容应该可以理解。
  mongodb使用内存映射的方式来访问和修改数据库文件,内存由操作系统来管理。开启journal的情况,数据文件映射到内存2个view:private view和write view。对write view的更新会刷新到磁盘,而对private view的更新不刷新到磁盘。写操作先修改private view,然后批量提交(groupCommit),修改write view。
  WriteIntent
发生写操作时,会记录修改的内存地址和大小,由结构WriteIntent表示。


DSC0001.gif DSC0002.gif


/** Declaration of an intent to write to a region of a memory mapped view
*  We store the end rather than the start pointer to make operator< faster
*    since that is heavily used in set lookup.
*/
struct WriteIntent { /* copyable */
void *p;      // intent to write up to p
unsigned len; // up to this len
void* end() const { return p; }
bool operator < (const WriteIntent& rhs) const { return end() < rhs.end(); } // 用于排序
};
WriteIntent  查看代码会发现大量的类似调用,这就是保存WriteIntent。
  getDur().writing(..)
getDur().writingPtr(...)
  CommitJob
CommitJob保存未批量提交的WriteIntent和DurOp,目前只使用一个全局对象commitJob。对于不修改数据库文件的操作,如创建文件(FileCreatedOp)、删除库(DropDbOp),不记录WriteIntent,而是记录DurOp。
  ThreadLocalIntents
由于mongodb是多线程程序,同时操作CommitJob需要加锁(groupCommitMutex)。为了避免频繁加锁,使用了线程局部变量





/** so we don't have to lock the groupCommitMutex too often */
class ThreadLocalIntents {
enum { N = 21 };
std::vector intents;
};
ThreadLocalIntents  WriteIntent先存放到intents里,当intents的大小达到N时,就添加到CommitJob里,这时候要才需要加锁。添加intents到CommitJob时,会对重叠的内存地址段进行合并,减少WriteIntent的数量。当然,CommitJob也会对添加的WriteIntent进行检查是否重复添加。这里有一个问题,如果intents的大小没有达到N,是不是永远都不添加到CommitJob里呢?不会。因为每次写操作,必须先获得'w'锁(库的写锁)或者'W'锁(全局写锁),当释放锁的时候,也会把intents添加到全局的数组里。
  何时groupCommit
写操作会先修改private view,并保存WriteIntent到CommitJob。但是private view是不持久化的,CommitJob保存的WriteIntent何时groupCommit?





const unsigned UncommittedBytesLimit = (sizeof(void*)==4) ? 50 * 1024 * 1024 : 100 * 1024 * 1024;
UncommittedBytesLimit

  • durThread线程定期groupCommit,间隔时间可以由journalCommitInterval选项指定。默认是100毫秒(journal文件所在硬盘分区和数据文件所在硬盘相同)或者30毫秒。另外,如果有线程在等待groupCommit完成,或者未commit的字节数大于UncommittedBytesLimit / 2,会提前commit。
  • 调用commitIfNeeded。如果未commit的字节数不小于UncommittedBytesLimit,或者是强制groupCommit,则执行groupCommit。
  groupCommit的过程
1.PREPLOGBUFFER
首先是生成写操作日志(redo log)。对WriteIntent从小到大排序,这样可以对前后的WriteIntent进行重叠、重复的合并。对每个WriteIntent的地址,和每个数据文件的private view的基地址进行比较(private view的基地址已经排序,查找很快),找出其隶属的数据文件的标号。WriteIntent的地址减掉private view的基地址得到偏移,再从private view把修改的数据复制下来。这样数据文件标号、偏移、数据,形成一个JEntry。
2.WRITETOJOURNAL
把写操作日志压缩并写入journal文件。这一步完成之后,即使mongodb异常退出,数据也不会丢失了,因为可以根据journal文件中的写操作日志重建数据。关于journal文件可以参见这里。
3.WRITETODATAFILES
把所有写操作更新到write view中。后台线程DataFileSync会定期把write view刷新到磁盘中,默认是60秒,由syncdelay选项指定。
4.REMAPPRIVATEVIEW
private view是copy on write的,即在发生写时开辟新的内存,否则是和write view共用一块内存的。如果写操作很频繁,则private view会申请很多的内存,所以private view会remap,防止占用内存过多。并不是每次groupCommit都会remap,只有持有'W'锁的情况下才会remap。
  durThread线程的定期groupCommit有三种情况会remap


  • privateMapBytes >= UncommittedBytesLimit
  • 前面9次groupCommit都没有ramap
  • durOptions选项指定了DurAlwaysRemap

调用commitIfNeeded发生的groupCommit,如果持有持有'W'锁则remap。
remap的一个问题
在_REMAPPRIVATEVIEW()函数中,有这样一段代码





#if defined(_WIN32) || defined(__sunos__)
// Note that this negatively affects performance.
// We must grab the exclusive lock here because remapPrivateView() on Windows and
// Solaris need to grab it as well, due to the lack of an atomic way to remap a
// memory mapped file.
// See SERVER-5723 for performance improvement.
// See SERVER-5680 to see why this code is necessary on Windows.
// See SERVER-8795 to see why this code is necessary on Solaris.
            LockMongoFilesExclusive lk;
#else
LockMongoFilesShared lk;
#endif
View Code  执行remap时,需要LockMongoFiles锁。win32下,这把锁是排他锁;而其他平台下(linux等)是共享锁。write view刷新到磁盘的时候,也需要LockMongoFiles共享锁。这样,在win32下,如果在执行磁盘刷新操作,则remap操作会被阻塞;而在执行remap之前,已经获得了'W'锁,这样会阻塞所有的读写操作。因此,在win32平台下,太多的写操作(写操作越多,remap越频繁)会导致整个数据库读写阻塞。
  在win32和linux下做了一个测试,不停的插入大小为10k的记录。结果显示如下:上图win32平台,下图为linux平台;横坐标为时间轴,从0开始;纵坐标为每秒的插入次数。很明显的,linux平台的性能比win32好很多。
DSC0003.png
DSC0004.png

运维网声明 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-84005-1-1.html 上篇帖子: MongoDB 3.0 Release Notes 下篇帖子: MongoDB的安装以及PHP扩展
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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