11

即使打开了日记功能,在 MongoDB 中是否还有机会丢失写入?

“默认情况下,丢失写入的最大程度,即那些未写入日志的写入,是在最后 100 毫秒内写入的写入。”

这是来自Manage Journaling,这表明您可能会丢失自上次将日志刷新到磁盘以来所做的写入。

如果我想要更高的持久性,“要强制 mongod 更频繁地提交日志,可以指定 j:true。当一个带有 j:true 的写操作处于挂起状态时,mongod 会将 journalCommitInterval 减少到设置值的三分之一。”

即使在这种情况下,将日志刷新到磁盘似乎也是异步的,因此仍有可能丢失写入。我是否遗漏了有关如何保证写入不会丢失的内容?

4

5 回答 5

19

发布一个新的答案来清理它。我进行了测试并再次阅读了源代码,我确信这种恼怒来自写关注文档中的一个不幸的句子。启用日志和j:true写入关注后,写入是持久的,并且没有神秘的数据丢失窗口。

即使打开了日记功能,在 MongoDB 中是否还有机会丢失写入?

是的,因为持久性还取决于各个操作的写入关注点。

“默认情况下,丢失写入的最大程度,即那些未写入日志的写入,是在最后 100 毫秒内写入的写入。”

这是来自 Manage Journaling,这表明您可能会丢失自上次将日志刷新到磁盘以来所做的写入。

那是对的。日志由一个单独的线程异步刷新,因此您可能会丢失自上次刷新以来的所有内容。

如果我想要更高的持久性,“要强制 mongod 更频繁地提交日志,您可以指定j:true. 当一个 write 操作j:true挂起时,mongod 将减少journalCommitInterval到设置值的三分之一。”

这也激怒了我。这就是它的意思:

当您使用 发送写操作时j:true,它不会立即触发磁盘刷新,也不会在网络线程上触发。这是有道理的,因为可能有几十个应用程序与同一个 mongod 实例通信。如果每个应用程序都大量使用日志,那么数据库会非常慢,因为它一直在进行 fsync。

相反,“持久性线程”将获取所有待处理的日志提交并将它们刷新到磁盘。线程是这样实现的(我的评论):

sleepmillis(oneThird); //dur.cpp, line 801
for( unsigned i = 1; i <= 2; i++ ) {
  // break, if any j:true write is pending
  if( commitJob._notify.nWaiting() )
    break;
  // or the number of bytes is greater than some threshold
  if( commitJob.bytes() > UncommittedBytesLimit / 2  )
    break;
  // otherwise, sleep another third
  sleepmillis(oneThird);
}

// fsync all pending writes                                      
durThreadGroupCommit();

因此,挂起的j:true操作将导致日志提交线程比正常情况更早地提交,并且它将所有挂起的写入提交到日志,包括那些没有j:true设置的写。

即使在这种情况下,将日志刷新到磁盘似乎也是异步的,因此仍有可能丢失写入。我是否遗漏了有关如何保证写入不会丢失的内容?

getLastError带有j:true日志写入问题的写入(或命令)将等待持久性线程完成同步,因此不存在数据丢失的风险(就操作系统和硬件而言)。

句子“但是,当写入操作不完全持久时,在日志提交之间存在一个窗口”可能是指在启用日志的情况下运行的 mongod 接受使用j:true写入问题的写入。在这种情况下,自上次日志提交以来写入可能会丢失。

我为此提交了一份文档错误报告

于 2013-09-02T16:28:27.517 回答
3

也许。是的,它等待数据被写入,但根据文档,有一个“当写入操作不完全持久时,日志提交之间存在一个窗口”,不管是什么。我找不到他们指的是什么。

我将编辑后的答案留在这里,但我来回颠倒了自己,所以有点烦人:


这有点棘手,因为您可以拉很多杠杆:

您的 MongoDB 设置

假设日志已激活(默认为 64 位),日志将定期提交。如果日志和数据文件在同一个块设备上,则默认值为journalCommitInterval100 毫秒,否则为 30 毫秒(因此最好将日志放在单独的磁盘上)。

您也可以将其更改journalCommitInterval为低至 2ms,但这会增加写入操作的数量并降低整体写入性能。

写作问题

您需要指定一个写入关注点,告诉驱动程序和数据库等到数据写入磁盘。但是,这不会等到数据实际写入磁盘,因为在默认设置的坏情况下,这将花费 100 毫秒。

所以,在最好的情况下,有一个 2ms 的窗口,数据可能会丢失。然而,这对于许多应用程序来说是不够的。

fsync命令强制对所有数据文件进行磁盘刷新,但如果您使用日志,则没有必要这样做,而且效率低下。

现实生活中的耐用性

即使您要记录每次写入,但如果数据中心管理员遇到糟糕的一天并在您的硬件上使用电锯,或者硬件只是自行解体,这有什么好处?

冗余存储,不是像 RAID 这样的块设备级别,而是更高级别的冗余存储对于许多场景来说是一个更好的选择:将数据放在不同的位置,或者至少在不同的机器上使用副本集,并使用w:majority启用日志的写入关注(不过,日志仅适用于主节点)。在个别机器上使用 RAID 来增加你的运气。

这提供了性能、耐用性和一致性的最佳折衷。此外,它允许您为每次写入调整写入关注点,并且具有良好的可用性。如果数据在三台不同的机器上排队等待下一次 fsync,那么任何一台机器上的下一次日志提交可能仍然需要 30 毫秒(最坏情况),但三台机器在 30 毫秒间隔内停机的可能性可能是百万倍低于电锯屠杀管理场景。

证据

TL; DR:我认为我上面的回答是正确的。

文档可能有点烦人,尤其是关于wtimeout,所以我检查了源代码。我不是 mongo 来源的专家,所以对此持保留态度:

write_concern.cpp中,我们发现(为简洁而编辑):

if ( cmdObj["j"].trueValue() ) {
    if( !getDur().awaitCommit() ) {
        // --journal is off
        result->append("jnote", "journaling not enabled on this server");
    } // ...
}
else if ( cmdObj["fsync"].trueValue() ) {
    if( !getDur().awaitCommit() ) {
        // if get here, not running with --journal
        log() << "fsync from getlasterror" << endl;
        result->append( "fsyncFiles" , MemoryMappedFile::flushAll( true ) );
    }

注意调用MemoryMappedFile::flushAll( true )iffsync已设置。这个调用显然不在第一个分支中。否则,持久性将在单独的线程上处理(相关文件前缀为dur_)。

这就解释了什么wtimeout是for:它是指等待slave的时间,与服务器上的I/O或fsync无关。

于 2013-08-28T13:29:15.030 回答
2

日志是为了使特定 mongod 上的数据保持一致状态,即使在电锯疯狂的情况下,但是通过 writeconcern 的客户端设置,它可以用来强制消除持久性。关于写关注DOCS

有一个选项,你可以在这里j:1阅读,它确保特定的写操作等待确认,直到它被写入磁盘上的日志文件(所以不仅仅是在内存映射中)。然而,这个文档说相反。:) 我会投票给第一个案例,这让我感觉更舒服。

如果您使用此类选项运行大量命令,mongodb 将调整日志提交间隔的大小以加快速度,您可以在此处阅读:DOCS这个您也提到过,正如其他人已经说过的,您可以指定一个间隔在 2-300 毫秒之间。

在我看来,持久性比 w:2 选项更可靠,而如果更新/写入操作由副本集中的两个成员确认,则实际上不太可能在同一分钟内丢失两者(数据文件刷新间隔),但并非不可能。

使用这两个选项将导致这样的情况,即当数据库集群确认操作时,它将驻留在内存中两个不同的盒子上,并且在一个盒子上它也将位于一致的可恢复磁盘位置。

于 2013-08-28T14:25:18.280 回答
0

通常丢失写入是每个系统中的一个问题,其中系统运行时和永久(非易失性)存储之间涉及缓冲/缓存/延迟写入,即使在操作系统级别(例如后写缓存)也是如此。因此,即使您的具体提供程序 (MongoDB) 提供了事务持久性功能,它总是有机会丢失写入,它是负责最终写入数据的底层操作系统,即使这样,设备级别也存在缓存......并且这只是较低的级别,使系统高度并发、分布式高性能只会让事情变得更糟。

简而言之,没有绝对的持久性,只有实用的/最终的/希望最好的持久性,尤其是像 Mongo 这样的 NoSQL 存储,它首先不是为了一致性和持久性而设计的。

于 2013-08-28T13:33:41.377 回答
0

我不得不同意 Sammaye 的观点,即日志与耐用性无关。但是,如果您想知道您是否真的可以信任 mongodb 以良好的一致性存储您的数据,那么我建议您阅读这篇博文。有来自 10gen 的关于该帖子的回复,以及作者对 10gen 帖子的回复。我建议您阅读它以做出明智的决定。我自己花了一些时间来理解所有细节,但这篇文章涵盖了基础知识。

制作 mongodb 的公司10gen 在这里给出了对博客文章的回复。

并且对回复的回复是教授在这个帖子上给出的。

它解释了很多关于 Mongodb 如何对数据进行分片,它是如何实际运行的,以及如果添加额外的安全锁对性能的影响。我强烈想说这三篇文章是最好的,也是迄今为止最全面的关于 mongodb 优缺点的文章,如果你认为它是片面的,看看评论,也看看人们不得不说什么,因为如果某件事得到了制作软件的公司的回复,那么它至少肯定有一些好处。

于 2013-08-28T13:17:00.807 回答