网络突然抽风?外网连接又出问题了!
MongoDB中的写入安全策略解析
MongoDB提供了一个名为write concern的设置,旨在确保写操作的可靠性。这一设置通常在客户端驱动程序中进行配置,并与db.getLastError()方法密切相关。
对于所有的MongoDB驱动程序来说,执行写操作(如插入、更新或删除)后,都会立即调用db.getLastError()方法。这样,客户端有机会了解写操作是否成功。如果在调用此方法时捕获到错误,客户端可以进行相应的处理,如记录日志、抛出异常或等待一段时间后重试。MongoDB服务器本身并不关心这些,它只是负责在发生错误时通知客户端。
这里需要注意两点:
1. db.getLastError()方法是由驱动程序自动调用的,因此业务代码不需要显式调用。
2. 驱动程序一定会调用db.getLastError(),但不一定能够捕获到错误。这主要取决于write concern的设置级别,这也是本文的主题。
以下是关于不同write concern级别的解析:
write concern:0(Unacknowledged)
在此级别下,驱动程序调用getLastError()后,MongoDB会立即返回结果,然后才会实际执行写操作。getLastError()的返回值一定是null,即使之后的Ap操作发生错误,驱动程序也无法知道。此级别的write concern能使写入调用立即返回,因此性能最佳,但可靠性最差。并不推荐使用。在各平台最新版本的驱动程序中,也不再将0作为默认级别。还有一个w:-1的级别,其特性与w:0相似,但不会捕获任何错误。
write concern:1(acknowledged)
此级别与Unacknowledged级别的区别在于,MongoDB只有在Ap(实际写入操作)完成后,才会返回getLastError()的响应。如果写入过程中发生错误,驱动程序可以捕获到并进行处理。此级别的write concern具有基本可靠性,也是MongoDB的默认设置级别。
write concern:1 & journal:true(Jounaled)
Acknowledged级别的write concern并非绝对可靠。因为MongoDB的Ap操作是将数据写入内存,然后定期通过fsync写入硬盘。如果在Ap之后、fsync之前MongoDB崩溃或服务器故障,持久化操作可能会失败。在w:1的级别下,驱动程序无法捕获到这种情况下的错误(因为响应已在Ap之后返回到驱动程序)。
为了解决这个问题,MongoDB使用了Journal机制。写操作在写入内存后,还会写入journal文件中。如果MongoDB非正常关闭,重启后可以根据journal文件的内容恢复写操作。在驱动程序层面,除了设置w:1外,还需要设置journal:true或j:true,以捕获这种情况下的错误。
write concern:2(Replica Acknowledged)
此级别仅在replica set的部署模式下生效。在此级别下,只有当secondary节点从primary节点完成复制后,getLastError()的结果才会返回。也可以同时设置journal:true或j:true,等待journal写入成功后才会返回结果。但请注意,只要primary节点的journal写入完成就会返回结果,无需等待secondary节点的journal也完成写入。类似地,可以设置w:3表示至少要有3个节点有数据,或者w:majority表示大于1/2的节点有数据。对于小规模集群(如3节点部署),通常配置w:2即可。
关于建议:
设置write concern级别实际上是在写操作的性能和可靠性之间进行权衡。写操作的等待时间越长,可靠性就越高。对于非关键数据,建议使用默认的w:1设置。对于关键数据,建议使用w:1 & j:true。需要注意的是,无论是否设置j:true,服务器端的journal都是默认开启的。驱动程序在每个写操作后都会隐式调用db.getLastError(),区别在于是否能够捕获到错误。
关于代码示例:
通常情况下,开发者无需自行调用db.getLastError()函数,因为驱动程序会在每个写操作后自动调用此方法。以下是一个示例代码:
db.collection("test", {}, function (err, collection) {
collection.insert({name: "world "}, function (err, result) {
assert.equal(null, err); // 如果没有错误发生,err参数将为null