通过 findAndModify 进行线性化读取

Overview

从副本集读取数据时,有可能读取过时的数据(即可能不反映读取操作之前发生的所有写操作)或不持久的数据(即数据的状态可能反映未进行写操作的数据)。由多数或副本集成员确认,因此可以回滚),具体取决于所使用的读取关注点。

从 3.4 版开始,MongoDB 引入了"linearizable"读取关注,该关注返回的数据不是陈旧的持久数据。 Linearizable读取关注保证仅在读取操作指定唯一标识单个文档的查询过滤器时适用。

本教程概述了另一种过程,该过程用于使用 MongoDB 3.2 进行部署的一种方法,其中一种使用db.collection.findAndModify()读取不是陈旧且无法回滚的数据。对于 MongoDB 3.4,尽管可以应用概述的过程,但请参阅"linearizable"阅读关注。

通过 findAndModify 进行线性化的读取

此过程使用db.collection.findAndModify()读取陈旧且无法回滚的数据。为此,该过程将findAndModify()方法与write concern结合使用来修改文档中的虚拟字段。具体来说,该过程要求:

Important

与仅使用"majority"的读取关注相比,“仲裁读取”过程具有大量成本,因为它会导致写入延迟而不是读取延迟。仅在绝对不能过时的情况下才应使用此技术。

Prerequisites

本教程从名为products的集合中读取。使用以下操作初始化集合。

db.products.insert( [
   {
     _id: 1,
     sku: "xyz123",
     description: "hats",
     available: [ { quantity: 25, size: "S" }, { quantity: 50, size: "M" } ],
     _dummy_field: 0
   },
   {
     _id: 2,
     sku: "abc123",
     description: "socks",
     available: [ { quantity: 10, size: "L" } ],
     _dummy_field: 0
   },
   {
     _id: 3,
     sku: "ijk123",
     description: "t-shirts",
     available: [ { quantity: 30, size: "M" }, { quantity: 5, size: "L" } ],
     _dummy_field: 0
   }
] )

此集合中的文档包含一个名为_dummy_field的虚拟字段,该字段在本教程中将以db.collection.findAndModify()递增。如果该字段不存在,则db.collection.findAndModify()操作会将其添加到文档中。该字段的目的是确保db.collection.findAndModify()导致对文档的修改。

Procedure

创建唯一索引。

在用于在db.collection.findAndModify()操作中指定完全匹配的字段上创建唯一索引。

本教程将在sku字段上使用完全匹配。因此,请在sku字段上创建唯一索引。

db.products.createIndex( { sku: 1 }, { unique: true } )

使用 findAndModify 读取提交的数据。

使用db.collection.findAndModify()方法对您要阅读的文档进行简单的更新,然后返回修改后的文档。需要写一个{ w: "majority" }。要指定要阅读的文档,必须使用唯一索引支持的完全匹配查询。

以下findAndModify()操作在唯一索引字段sku上指定完全匹配,并在匹配文档中递增名为_dummy_field的字段。虽然不是必需的,但此命令的写关注点还包括_5 毫秒的wtimeout值,以防止在写不能传播到大多数有投票权的成员的情况下永远阻止该操作。

var updatedDocument = db.products.findAndModify(
   {
     query: { sku: "abc123" },
     update: { $inc: { _dummy_field: 1 } },
     new: true,
     writeConcern: { w: "majority", wtimeout: 5000 }
   }
);

即使在副本集中的两个节点认为它们是主要节点的情况下,也只有一个节点能够使用w: "majority"完成写入。这样,只有当 Client 端已连接到 true 的主节点以执行操作时,具有"majority"写入关注的findAndModify()方法才会成功。

由于仲裁读取过程只会增加文档中的虚拟字段,因此您可以安全地重复调用findAndModify(),并根据需要调整wtimeout