读隔离,一致性和新版本(Recency)


进入MongoDB中文手册(4.2版本)目录

1 隔离保证

1.1 读未提交

根据读策略,客户端可以在持久化(durable)之前看到写入结果:

  • 不管写策略如何,其他使用"local"或"available" 读策略的客户端都可以在发起的客户端收到写操作的确认消息之前看到写操作的结果。
  • 使用"local"或"available" 读策略的客户端可以读取数据,这些数据随后可能会在副本集故障转移期间回滚。

对于多文档事务中的操作,在提交事务时,将保存在事务中进行的所有数据的更改,并在事务外部可见。也就是说,一个事务在回滚其他事务时将不会提交其某些更改。
在提交事务之前,在事务外部看不到在事务中进行的数据更改。
但是,当一个事务写入多个分片时,并非所有外部读取操作都需要等待已提交事务的结果在所有分片上可见。例如,如果提交了一个事务,并且在分片A上可以看到写1,但是在分片B上仍然看不到写2,则外部读策略 "local"可以读取写1的结果而看不到写2。
读未提交是默认的隔离级别,适用于 mongod单机实例以及副本集和分片群集。

1.2 读未提交和单文档原子性

对于单个文档,写操作是原子的。即,如果写操作正在更新文档中的多个字段,则读操作将永远不会看到仅更新了某些字段的文档。但是,尽管客户端可能看不到部分更新的文档,但读未提交意味着在更改内容持久化之前仍然能看到更新的文档。
对于单机的mongod实例,对单个文档的一组读取和写入操作是可序列化的。使用副本集,仅在没有回滚的情况下,对单个文档的一组读取和写入操作才能序列化 。

1.3 读未提交和多文档写入

当单个写入操作(例如 db.collection.updateMany())修改多个文档时,每个文档的修改都是原子的,但整个操作不是原子的。
当执行多文档写操作时,无论是通过单个写操作还是通过多个写操作,其他操作都可能会交错执行。
当对多个文档(在单个或多个集合中)进行读写需要原子性操作的情况下,MongoDB支持多文档事务:

  • 在版本4.0中,MongoDB支持副本集上的多文档事务。
  • 在4.2版中,MongoDB引入了分布式事务,它增加了对分片群集上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。

有关MongoDB中事务的详细信息,请参阅事务页面。

重要
在大多数情况下,与单文档写入相比,多文档事务产生的性能成本更高,并且多文档事务的可用性不应代替有效的架构设计。在许多情况下, 非规范化数据模型(嵌入式文档和数组)对于您的数据和用例将是一直最佳的。也就是说,在许多情况下,对数据进行适当的建模将最大程度地减少对多文档交易的需求。

有关其他事务使用方面的注意事项(例如运行时限制和oplog大小限制),另请参见生产注意事项

在不隔离多文档写入操作的情况下,MongoDB表现出以下行为:

  1. 非时间点(Non-point-in-time)读取操作。假设读取操作在时间t1开始并开始读取文档。然后,写操作在稍后的某个时间t2提交对其中一个文档的更新。读操作可能会看到文档的更新版本,因此看不到数据的时间点(point-in-time)快照。
  2. 不可序列化的操作。假设读取操作在时间t1读取文档d1,而写入操作在稍后的时间t3更新d1。这引入了读写依赖(read-write dependency),因此,如果要序列化操作,则读取操作必须先于写入操作。但是还假设写操作在时间t2更新文档d2,而读取操作随后在稍后的时间 t4读取d2。这就引入了写-读依赖(write-read dependency),它将要求读操作在可序列化计划中的写操作之后。这就有了一个依赖循环,导致可序列化成为不可能。
  3. 读取操作可能会丢失在读取操作过程中更新的匹配文档。

1.4 游标快照

在某些情况下,MongoDB游标可以多次返回同一文档。当游标返回文档时,其他操作可能会与查询交错执行。如果其中某些操作更改了查询使用的索引上的索引字段;那么游标将多次返回同一文档。
如果您的集合具有一个或多个从不会修改的字段,则可以在该字段或这些字段上使用唯一索引,以便查询的时候每个文档可以返回多次。查询使用hint()以显式的方式强制查询使用该索引。

2 单调写(Monotonic Writes)

默认情况下,MongoDB为单机mongod实例和副本集提供单调写保证。
有关单调写入和分片群集,请参见因果一致性

3 实时顺序(Real Time Order)

3.4版的新功能。
对于主节点上的读取和写入操作,发出具有"linearizable"读策略的读操作和具有"majority"写策略的写操作使多个线程可以在单个文档上执行读取和写入,就好像单个线程实时执行了这些操作一样。也就是说,这些读写的相应计划被认为是线性的。

可以看看:因果一致性。

4 因果一致性(Causal Consistency)

3.6版的新功能。
如果操作在逻辑上依赖于先前的操作,则这些操作之间存在因果关系。例如,基于指定条件删除所有文档的写入操作和验证删除操作的后续读取操作具有因果关系。
在因果一致的会话中,MongoDB按照尊重因果关系的顺序执行因果操作,并且客户端观察到的结果与因果关系一致。

4.1 客户端会话和因果一致性保证

为了提供因果一致性,MongoDB 3.6启用了客户端会话中的因果一致性。因果一致的会话表示具有"majority" 读策略的读操作和具有"majority"写策略的写操作的关联序列具有因果关系,这由它们的顺序反映出来。应用程序必须确保一次只有一个线程在客户端会话中执行这些操作。
对于因果相关的操作::

  1. 客户端启动客户端会话。
    重要提示:
    客户会话仅保证以下方面的因果一致性:
    (1)用具有"majority"读策略的读操作; 也就是说,返回数据已被大多数副本集成员确认并且是持久化的;
    (2)用具有"majority"写策略写操作;也就是说,写操作要求确认该操作已应用于副本集中有投票权的成员的大多数。
    有关因果一致性和各种读写问题的更多信息,请参见因果一致性和读写策略
  2. 当客户端发出一序列具有"majority"读策略的读操作和具有 "majority"写策略的写操作时 ,客户端将包含每个操作的会话信息。
  3. 对于与会话相关联的每个具有"majority"读策略的读操作和具有"majority"写策略的写操作,即使操作错误,MongoDB也会返回操作时间和集群时间。客户端会话跟踪操作时间和群集时间。
    注意:
    (1)对于未确认(w: 0)的写操作,MongoDB不返回操作时间和集群时间。未经确认的写入并不表示任何因果关系。
    (2)尽管MongoDB在客户端会话中返回读操作和已确认写操作的操作时间和群集时间,但是只有具有"majority"读策略的读操作和具有"majority"写策略的写操作才能保证因果一致性。有关详细信息,请参见因果一致性和读写策略
  4. 关联的客户端会话跟踪这两个时间字段。
    注意:
    不同会话之间的操作可以因果一致。MongoDB驱动程序和mongo shell提供了延长客户端会话的操作时间和集群时间的方法。因此,客户端可以提前群集时间和一个客户端会话的操作时间,使其与另一客户端会话的操作保持一致。

4.2 因果一致性保证

下表列出了因果一致会话提供的因果一致性保证,涉及具有majority"读策略"的读操作和具有"majority"写策略的写入操作。

保证(Guarantees)描述
阅读您的写入内容读操作反映了在其之前的写操作的结果。
单调读(Monotonic reads)读取操作不会返回比先前读取操作更早的数据状态。

例如,如果在会话中:
 * write1优先于write2;
 * read1优先于read2;
 * read1返回write2的结果。
那么,read2不会返回write1的结果。
单调写(Monotonic writes)其他写入操作之前执行的写操作必须在这些写入操作之前执行。

例如,如果在会话中write1必须先于write2,则在write2的数据的状态必须在write1的数据状态之后显示。其他写入可以在write1和write2之间交错执行,但是write2不能在write1之前发生。
写在读后(Writes follow reads)写操作必须在读操作之后发生。即,写入时的数据状态必须包含之前的读取操作的数据状态。

4.3 读优先(Read Preference)

这个保证适用于MongoDB部署的所有成员。例如,如果在因果一致的会话中发出具有"majority"写策略的写操作, 然后发出从具有"majority"读策略的次节点(即次节点读优先) 读取的读操作,则读操作将反映写操作之后的数据库状态。

4.4 隔离性

因果一致的会话内的操作与会话外的操作不是隔离的。如果并发的写操作在会话的写操作和读操作之间交错执行,则会话的读操作可能返回反映在会话的写操作之后发生的写操作的结果。

4.5 特性兼容版本

featureCompatibilityVersion(FCV)必须设置为“3.6”或更大。要检查fCV,请运行以下命令:

db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

有关更多信息,请参见View FeatureCompatibilityVersionsetFeatureCompatibilityVersion

4.6 MongoDB驱动程序

提示
应用程序必须确保一次只有一个线程在客户端会话中执行这些操作。

客户端需要将MongoDB驱动程序更新为MongoDB 3.6或更高版本:

  • Java 3.6+
  • Python 3.6+
  • C 1.9+
  • C# 2.5+
  • Node 3.0+
  • Ruby 2.5+
  • Perl 2.0+
  • PHPC 1.4+
  • Scala 2.2+

4.7 示例

重要
因果一致的会话只能保证读策略"majority"的读操作和写策略"majority"的写操作的因果一致性 。

考虑一个集合items,该集合维护各种项目的当前和历史数据。只有历史数据具有非空end日期。如果项目的sku值更改,则具有旧sku值的文档需要用end日期更新 ,此后,将使用当前sku值插入新文档 。客户端可以使用因果一致的会话来确保更新在插入之前发生。

// Example 1: Use a causally consistent session to ensure that the update occurs before the insert.
ClientSession session1 = client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build());
Date currentDate = new Date();
MongoCollection<Document> items = client.getDatabase("test")
        .withReadConcern(ReadConcern.MAJORITY)
        .withWriteConcern(WriteConcern.MAJORITY.withWTimeout(1000, TimeUnit.MILLISECONDS))
        .getCollection("test");

items.updateOne(session1, eq("sku", "111"), set("end", currentDate));

Document document = new Document("sku", "nuts-111")
        .append("name", "Pecans")
        .append("start", currentDate);
items.insertOne(session1, document);

如果另一个客户端需要读取所有当前sku值,则可以将群集时间和操作时间提前到另一个会话的群集时间和操作时间,以确保该客户端与另一个会话有因果关系,并在两次写入之后读取:

// Example 2: Advance the cluster time and the operation time to that of the other session to ensure that
// this client is causally consistent with the other session and read after the two writes.
ClientSession session2 = client.startSession(ClientSessionOptions.builder().causallyConsistent(true).build());
session2.advanceClusterTime(session1.getClusterTime());
session2.advanceOperationTime(session1.getOperationTime());

items = client.getDatabase("test")
        .withReadPreference(ReadPreference.secondary())
        .withReadConcern(ReadConcern.MAJORITY)
        .withWriteConcern(WriteConcern.MAJORITY.withWTimeout(1000, TimeUnit.MILLISECONDS))
        .getCollection("items");

for (Document item: items.find(session2, eq("end", BsonNull.VALUE))) {
    System.out.println(item);
}

4.8 局限性

以下构建内存结构的操作在因果上不一致:

操作备注
collStats
$collStats和latencyStats选项
$currentOp如果操作与因果一致的客户端会话相关联,则返回错误。
createIndexes
dbHash从MongoDB 4.2开始
dbStats
getMore如果操作与因果一致的客户端会话相关联,则返回错误。
$indexStats
mapReduce从MongoDB 4.2开始
ping如果操作与因果一致的客户端会话相关联,则返回错误。
serverStatus如果操作与因果一致的客户端会话相关联,则返回错误。
validate从MongoDB 4.2开始

进入MongoDB中文手册(4.2版本)目录

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值