即使日记功能已开启,MongoDB中仍有机会丢失写入内容吗?
“默认情况下,丢失写入的最大程度,即未对日志进行的写入,是在最后100毫秒内完成的。”
这是来自Manage Journaling,表示自上次将日志刷新到磁盘后,您可能会丢失写入内容。
如果我想要更强的持久性,“为了强制mongod更频繁地提交日志,你可以指定j:true。当j:true的写操作挂起时,mongod会将journalCommitInterval减少到设定值的三分之一。“
即使在这种情况下,看起来将日志刷新到磁盘也是异步的,因此仍有机会丢失写入。我是否遗漏了一些关于如何保证写入不丢失的内容?
答案 0 :(得分:19)
发布新答案以清理它。我进行了测试并再次阅读了源代码,我确信这种烦恼来自write concern documentation中的一个不幸的句子。启用日记功能并且j:true
写入问题时,写入是持久的,并且没有神秘的数据丢失窗口。
即使日记功能已开启,MongoDB中仍有机会丢失写入内容吗?
是的,因为耐久性还取决于个人操作的写作问题。
“默认情况下,丢失写入的最大程度,即未对日志进行的写入,是在最后100毫秒内完成的。”
这来自Manage Journaling,它表示自上次将日志刷新到磁盘后您可能会丢失写入内容。
这是正确的。该日志被一个单独的线程异步刷新,因此自上次刷新以来你可能会丢失所有内容。
如果我想要更强的持久性,“要强制mongod更频繁地提交日志,您可以指定
j:true
。当j:true
的写入操作处于待处理状态时,mongod将减少{{1} }到设定值的三分之一。“
这也激怒了我。这就是它的含义:
当您使用journalCommitInterval
发送写入操作时,它不会立即触发磁盘刷新,也不会在网络线程上触发。这是有道理的,因为可能有数十个应用程序与同一个mongod实例进行通信。如果每个应用程序都经常使用日志,那么db会非常慢,因为它一直都在使用。
相反,会发生的事情是'持久性线程'将采用所有挂起的日志提交并将它们刷新到磁盘。线程是这样实现的(评论我的):
j:true
因此,挂起的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
命令)将等待持久性线程完成同步,因此不存在数据丢失的风险(至于操作系统和硬件保证)。
句子“但是,当写入操作不完全持久时,日志提交之间有一个窗口”可能是指运行启用了日记功能的mongod接受写入 NOT 使用{ {1}}写下问题。在这种情况下,自上次提交日志以来,写入可能会丢失。
我为此提交了docs bug report。
答案 1 :(得分:3)
也许。是的,它等待写入数据,但根据文档,有'there is a window between journal commits when the write operation is not fully durable',无论是什么。我无法找出他们所指的内容。
我在这里留下编辑过的答案,但我来回反复,所以有点恼火:
这有点棘手,因为你可以提取很多杠杆:
您的MongoDB设置
假设已激活日记功能(默认为64位),日志将定期提交。如果日志和数据文件位于同一块设备上,则journalCommitInterval
的默认值为100毫秒;如果不是,则为30毫秒(因此最好将日志放在单独的磁盘上)。
您也可以将journalCommitInterval
更改为2毫秒,但这会增加写操作次数并降低整体写入性能。
写作关注
您需要指定一个写入问题,告诉驱动程序和数据库等待数据写入磁盘。 但是,这不会等到数据实际写入磁盘,因为在使用默认设置的情况下,这将需要100ms。
所以,在最好的,有一个2ms的窗口,数据可能会丢失。然而,这对于许多应用来说是不够的。
fsync
命令强制刷新所有数据文件,但如果使用日记功能则不需要这样做,而且效率低下。
现实生活中的耐久性
即使您要记录每次写入,如果数据中心管理员有糟糕的一天并且在您的硬件上使用电锯,或者硬件只是解体,那么它有什么好处呢?
冗余存储,不是像RAID这样的块设备级别,但是在更高级别上是许多场景的更好选择:使用副本集将数据放在不同位置或至少在不同机器上并使用w:majority
写入关注启用了日记功能(但日记功能仅适用于主要版本)。在各台机器上使用RAID可以增加运气。
这提供了性能,耐用性和一致性的最佳折衷。此外,它允许您调整每次写入的写入问题并具有良好的可用性。 如果数据在三台不同的机器上排队等待下一个fsync,那么任何机器上的下一个日志提交可能仍然是30ms(最坏的情况),但三台机器在30ms间隔内下降的可能性是可能比电锯 - 大屠杀 - 管理员情景低百万倍。
<强>证据强>
<击> 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 )
,请注意来电fsync
。这个电话显然不在第一个分支中。否则,在sepate线程(相关文件前缀为dur_
)上处理持久性。
击>
这解释了wtimeout
的用途:它指的是等待奴隶的时间,与服务器上的I / O或fsync无关。
答案 2 :(得分:2)
日志记录用于将特定mongod上的数据保持在一致状态,即使在电锯疯狂的情况下,但是通过writeconcern进行客户端设置,它可以用于强制持久性。关于写作关注DOCS。
有一个选项j:1
,您可以阅读here,确保特定的写操作等待确认,直到将其写入磁盘上的日志文件(因此不仅仅是记忆图)。然而,这个docs反过来说了。 :)我会投票给第一个让我感觉更舒服的案例。
如果你使用这样的选项运行许多命令,mongodb将调整日志的提交间隔的大小以加快速度,你可以在这里阅读:DOCS这个你也提到过的和其他已经提到的说你可以指定2-300ms之间的间隔。
我认为在w:2选项中更加确保持久性,而如果更新/写入操作由复制集中的两个成员确认,则实际上不太可能在同一分钟内丢失(数据文件刷新间隔),但不是不可能。
使用这两个选项将导致这样的情况:当数据库集群确认操作时,它将驻留在内存中的两个不同的框中,而且它也将位于一致的可恢复磁盘位置。
答案 3 :(得分:0)
我不得不同意Sammaye的观点,即追踪与耐久性无关。但是,如果你想得到一个答案,你是否真的可以信任mongodb存储你的数据具有良好的一致性,那么我建议你阅读这个blog post。 10gen对该帖子的回复以及作者对10gen帖子的回复。我建议你读它做出有根据的决定。我花了一些时间来了解我自己的所有细节,但这篇文章涵盖了基础知识。
博客文章的回复是here by 10gen,这是制作mongodb的公司。
教授on this post给出了对回复的回应。
它解释了很多关于Mongodb如何对数据进行分片,如何实际运行以及如果添加额外的安全锁所需的性能。我非常想说这三篇着作是最好的东西,到目前为止最全面的东西都谈到了mongodb的好处和缺点,如果你认为它是单方面的,看看评论,还看到人们不得不说,因为如果有人收到制作该软件的公司的回复,那么它必须至少取得一些优点。
答案 4 :(得分:0)
在系统的运行时和永久(非易失性)存储之间存在缓冲/缓存/延迟写入的每个系统中,即使在操作系统级别(例如,后写式缓存),通常丢失写入也是一个问题。 。因此,总是有机会丢失写入,即使您的具体提供程序(MongoDB)提供事务持久性功能,它也是负责最终写入数据的底层操作系统,即使这样,也有缓存设备级别...而这只是较低级别,使系统高度并发,分布式和性能只会让事情变得更糟。
简而言之,没有绝对的耐用性,只有实用/最终/希望最好的耐用性,特别是像Mongo这样的NoSQL存储,它首先不是为了一致性和耐用性而制造的。